<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.dolibarr.org/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Lrq3000</id>
	<title>Dolibarr ERP CRM Wiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.dolibarr.org/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Lrq3000"/>
	<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php/Special:Contributions/Lrq3000"/>
	<updated>2026-10-23T04:23:31Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.39.17</generator>
	<entry>
		<id>https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Dev&amp;diff=35474</id>
		<title>Module CustomFields Dev</title>
		<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Dev&amp;diff=35474"/>
		<updated>2016-05-10T21:55:28Z</updated>

		<summary type="html">&lt;p&gt;Lrq3000: /* General purpose SQL methods */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:CustomFields]]&lt;br /&gt;
{{TemplateDocDevEn}}&lt;br /&gt;
{{TemplateModEN}}&lt;br /&gt;
{{BasculeDevUserEn|&lt;br /&gt;
name=CustomFields|&lt;br /&gt;
num=8500|&lt;br /&gt;
devdoc=This page|&lt;br /&gt;
userdoc=[[Module_CustomFields]]|}}&lt;br /&gt;
[[Category:FAQ EN]]&lt;br /&gt;
&lt;br /&gt;
These notes are aimed to developpers who want to extend the functionnalities of the module or want to use it for extreme cases.&lt;br /&gt;
This is not meant to guide implementers on how to implement and configure the module (see the related chapter for that).&lt;br /&gt;
&lt;br /&gt;
If you are an implementer and just want to use CustomFields with its normal functions, you can read the [[Module_CustomFields|user documentation of CustomFields]].&lt;br /&gt;
&lt;br /&gt;
You can also jump directly to [[Module_CustomFields_Cases]] wiki page for a collection of practical examples.&lt;br /&gt;
&lt;br /&gt;
= Principles =&lt;br /&gt;
&lt;br /&gt;
CustomFields is simply a wrapper for SQL databases.&lt;br /&gt;
&lt;br /&gt;
The module is based on SQL to manage the fields and most of the stuff. It uses and understands standard SQL.&lt;br /&gt;
&lt;br /&gt;
The main principles of CustomFields are:&lt;br /&gt;
&lt;br /&gt;
* one SQL column = one Dolibarr field.&lt;br /&gt;
* one SQL type = one method to show and edit the field in Dolibarr.&lt;br /&gt;
* almost everything should be configurable from the configuration file, or admin interface.&lt;br /&gt;
* be independent from Dolibarr&#039;s data structures.&lt;br /&gt;
&lt;br /&gt;
CustomFields sits besides Dolibarr, and does not mix with it, even if in the Dolibarr&#039;s interface it looks like CustomFields is embedded inside Dolibarr, in reality, CustomFields is totally independent.&lt;br /&gt;
&lt;br /&gt;
= Goals =&lt;br /&gt;
&lt;br /&gt;
This module was made to provide an easy way to manage custom sql fields, not only for users and implementers, but also for developpers to use in their own module:&lt;br /&gt;
* by easily adding the support of custom fields for your own module by using this module;&lt;br /&gt;
* by reusing custom fields from another module in your own module;&lt;br /&gt;
* by managing sql fields in your own module by using the CustomFields class (which will take care of everything from printing, editing to PDF/ODT templating).&lt;br /&gt;
&lt;br /&gt;
So this module is not only an auxiliary of other modules, but it can also be the core part of your own module by using it as your SQL query class for fields.&lt;br /&gt;
&lt;br /&gt;
= SQL structure =&lt;br /&gt;
&lt;br /&gt;
A CustomFields table needs to respect three parameters that never change:&lt;br /&gt;
&lt;br /&gt;
* table&#039;s name (format: llx_*dolibarrmodule*_customfields where *dolibarrmodule* = table_element in conf_customfields.lib.php)&lt;br /&gt;
* first column: rowid, which is the primary key for each field&lt;br /&gt;
* second column: fk_*dolibarrmodule* which is the foreign key that links every customfields record to the Dolibarr&#039;s module that is associated (invoice, order, etc...).&lt;br /&gt;
&lt;br /&gt;
Apart from the table&#039;s name and the first two special columns, every other columns are just standard sql columns that you can freely create, edit or delete as you want, from the module&#039;s administration page or from a third-party SQL management tool (such as phpMyAdmin).&lt;br /&gt;
&lt;br /&gt;
You can also create more complex sql fields: constraints, foreign keys, transformations, other sql types, etc...).&lt;br /&gt;
&lt;br /&gt;
Then the module will try to manage the sql fields you manually created the best it can:&lt;br /&gt;
* by printing appropriate input fields in the Dolibarr gui interface.&lt;br /&gt;
* by providing the developper an easy access to these fields in php (eg: $customfields-&amp;gt;cf_myfield or $object-&amp;gt;customfields-&amp;gt;cf_myfield) for any use, including PDF templates and ODT templates, or in any php script (so that you can use these fields in your own module).&lt;br /&gt;
&lt;br /&gt;
= SQL Indexes =&lt;br /&gt;
&lt;br /&gt;
The module automatically adds an index on the primary key and foreign key (first and second columns).&lt;br /&gt;
&lt;br /&gt;
To further optimize the performances of your database, and particularly the customfields tables, you may want to manually add indexes based on usage.&lt;br /&gt;
&lt;br /&gt;
Also, you may add indexes on remote tables used by constrained fields, which could optimize even more the performances since constrained fields have the worst performance drawback for CustomFields.&lt;br /&gt;
&lt;br /&gt;
= SQL Compatibility mode =&lt;br /&gt;
&lt;br /&gt;
The module relies on referential integrity checks and foreign keys of your DBMS to keep the database clean and to provide the constrained fields feature. Referential integrity checks is part of the SQL standards and should be used whenever possible, which always was a main goal for CustomFields (to be respectful of SQL standards).&lt;br /&gt;
&lt;br /&gt;
Unluckily, a few DBMS systems still don&#039;t support this critical functionality of the SQL standard, for example MySQL enables MyISAM engine by default, which doesn&#039;t support this feature.&lt;br /&gt;
&lt;br /&gt;
In these cases, you can either do two things:&lt;br /&gt;
* Switch to an engine which support referential integrity. For example with MySQL, you can enable InnoDB, which support referential integrity checks and foreign keys.&lt;br /&gt;
* Use CustomFields compatibility mode (more infos below).&lt;br /&gt;
&lt;br /&gt;
In the case your database does not support referential integrity checks, CustomFields will automatically switch to the SQL compatibility mode, which will allow you to use CustomFields even on databases where foreign keys are not possible.&lt;br /&gt;
&lt;br /&gt;
In compatibility mode, you will get nearly all CustomFields advanced features such as Constrained fields, but the customfields&#039; tables in the database won&#039;t be cleaned automatically (no delete on cascade, meaning that when you delete an object, the linked custom fields will still be kept in your database), and constrained fields won&#039;t be updated automatically on update.&lt;br /&gt;
&lt;br /&gt;
Nevertheless, these missing cleanup functionalities won&#039;t be seen by your users because they will never be accessible: on deletion of an object, the custom fields won&#039;t ever be accessible again from Dolibarr&#039;s interface, and object&#039;s ids are never changed since Dolibarr also relies on this. Thus in compatibility mode, CustomFields will act just like normally, but your database will be a bit less clean.&lt;br /&gt;
&lt;br /&gt;
= JSON extension for extra options =&lt;br /&gt;
&lt;br /&gt;
== Introduction to JSON in CustomFields ==&lt;br /&gt;
The relational model is very great to store and fetch data with a declarative language, and this is very powerful, but this model also imposes a few limitations, for example the relations must be well formatted and predefined.&lt;br /&gt;
&lt;br /&gt;
That&#039;s why we finally implemented a &#039;&#039;&#039;JSON extension&#039;&#039;&#039; to manage &#039;&#039;&#039;extra options&#039;&#039;&#039; and associate them with custom fields. This allows to add many properties (or columns if you are more used to the relational model) on-the-fly without having to predefine them prior to storing and using them.&lt;br /&gt;
&lt;br /&gt;
This allows to add an undefined number of columns/properties/extra options that will help manage special behaviours for custom fields. For example, it is currently used to allow to change the order of appearance of custom fields, and to store and manage remote table and columns datas for constrained fields on DBMS that do not support Foreign Keys checks.&lt;br /&gt;
&lt;br /&gt;
We chose JSON over XML because the library are simpler and the data footprint smaller, and because CustomFields does not need advanced features of XML like XQuery, data typing and meta structures (at least not for now).&lt;br /&gt;
&lt;br /&gt;
The other advantage is that php objects can directly be encoded into JSON without any preprocessing, and can then as straightly be decoded from JSON back into a php object.&lt;br /&gt;
&lt;br /&gt;
A simple mean to encode and decode back an object into and from a storage place, as well as allow users to easily add/remove extra options were the main requirement, and JSON (+ JSON libraries for PHP) meet this criteria.&lt;br /&gt;
&lt;br /&gt;
However, note that the extra options are now encoded as an associative array, so as to avoid any ambiguities (there&#039;s no associative array in JSON, they are converted to objects).&lt;br /&gt;
&lt;br /&gt;
== Implementation of JSON ==&lt;br /&gt;
The implementation is very simple: a table called &#039;&#039;&#039;llx_customfields_extraoptions&#039;&#039;&#039; is created when you enable the CustomFields module, and this table only contains three columns:&lt;br /&gt;
* table_name (varchar)&lt;br /&gt;
* column_name (varchar)&lt;br /&gt;
* extraoptions (blob)&lt;br /&gt;
&lt;br /&gt;
(table_name, column_name) both forms the primary key of the table, and links to columns (custom fields) of other customfields tables.&lt;br /&gt;
extraoptions is a blob, which allows to store the data as binary (as-is) without encoding (which solves a big problem of JSON which is encoding special and foreign characters, here there is no problem at all).&lt;br /&gt;
&lt;br /&gt;
When fetching custom fields, the module simply JOIN the extraoptions table, which adds an &#039;&#039;&#039;extraoptions&#039;&#039;&#039; column for each custom field in the SQL result. Then it is quite easy to just json_decode() the data returned inside the extraoptions column.&lt;br /&gt;
&lt;br /&gt;
This methodology allows to have all the advantages of a relational database, with the flexibility of a free-schema model:&lt;br /&gt;
* everything is stored inside the database (only one storage place)&lt;br /&gt;
* fixed columns allows for relational joining and optimization&lt;br /&gt;
* thus queries (fetching) are optimised&lt;br /&gt;
* at the same time, the extraoptions are automatically retrieved and associated with every custom field, and whenever needed they can be used&lt;br /&gt;
* and thus we also get a flexible model to &#039;&#039;&#039;add extra (virtual) columns whenever needed&#039;&#039;&#039; without having to alter the relational database schema&lt;br /&gt;
&lt;br /&gt;
Thus no critical information is and should be stored inside the extraoptions column because they would not be query-able, only extra options that are just used to tweak the management of those fields inside the PHP application should be stored. But this is a wonderful way to add non critical informations that still allows to develop more complex features and management for special custom fields.&lt;br /&gt;
&lt;br /&gt;
== CustomFields JSON API ==&lt;br /&gt;
&lt;br /&gt;
=== Storing extra options ===&lt;br /&gt;
&lt;br /&gt;
You can use the &#039;&#039;&#039;setExtra()&#039;&#039;&#039; method to set extra options of a custom field.&lt;br /&gt;
&lt;br /&gt;
Eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$extra = array(); // $extra should always be an associative array, it&#039;s no longer an object&lt;br /&gt;
$extra[&#039;myproperty&#039;] = &#039;Anything I want here&#039;; // set a property/extra option like this&lt;br /&gt;
$extra[&#039;category&#039;][&#039;subcategory&#039;][&#039;prop&#039;] = true; // we can set recursive properties, there&#039;s not any limit to how deep the hierarchy can be&lt;br /&gt;
&lt;br /&gt;
require_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomField($db, &#039;currentmodule&#039;);&lt;br /&gt;
$fieldid = &#039;mycustomfields&#039;; // $fieldid can either be a column_name or an ordinal_position from information_schema.columns&lt;br /&gt;
$result = $customfields-&amp;gt;setExtra($fieldid, $extra); // store the extra options inside the database&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can also use addCustomField() or updateCustomField() with an $extra array, and these functions will also call the setExtra() method.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: the extra options you set are &#039;&#039;&#039;appended&#039;&#039;&#039; to the ones already stored in the database. However, you can also overwrite previous options, just use the same name of property but with a different value (the replacement is recursive, you can do it at any level in the hierarchy).&lt;br /&gt;
&lt;br /&gt;
=== Fetching extra options ===&lt;br /&gt;
&lt;br /&gt;
Extra options are automatically fetched along with a custom field structure, decoded, and stored inside the extra sub-property of any field object, or CustomFields object.&lt;br /&gt;
&lt;br /&gt;
When fetched, extra options are stored as a sub-object for the current field object (which itself is stored as a subobject of the CustomFields object if caching is enabled).&lt;br /&gt;
&lt;br /&gt;
Eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
require_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomField($db, &#039;currentmodule&#039;);&lt;br /&gt;
$fields = fetchAllFieldsStruct(); // fetching fields structure will automatically fetch extra options at the same time. This works with fetchAllFieldsStruct() and fetchFieldStruct()&lt;br /&gt;
&lt;br /&gt;
// Now that you fetched the fields structure, you can just access the extra options sub-properties:&lt;br /&gt;
print_r($fields-&amp;gt;mycustomfield-&amp;gt;extra); // will print all extra options for the field mycustomfield&lt;br /&gt;
print_r($customfields-&amp;gt;fields-&amp;gt;mycustomfield-&amp;gt;extra); // you can also access the same field(s) directly from the CustomFields object if caching is enabled (default behaviour)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Modifying extra options ===&lt;br /&gt;
&lt;br /&gt;
You can just combine the two previous points to edit extra options of a custom field: just load a custom field, edit on-the-fly the extra property with PHP code, and then store it back directly into the database:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
require_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomField($db, &#039;currentmodule&#039;);&lt;br /&gt;
$fields = fetchAllFieldsStruct();&lt;br /&gt;
&lt;br /&gt;
$fields-&amp;gt;mycustomfield-&amp;gt;extra[&#039;newproperty&#039;] = &#039;This is a new property&#039;; // add a new property over the old extra options sub-object&lt;br /&gt;
$fields-&amp;gt;mycustomfield-&amp;gt;extra[&#039;mycategory&#039;] = &#039;Overwrite the old category&#039;; // this will replace the previous value of &#039;mycategory&#039; extra option&lt;br /&gt;
&lt;br /&gt;
$customfields-&amp;gt;setExtra(&#039;mycustomfield&#039;, $fields-&amp;gt;mycustomfield-&amp;gt;extra); // store the modified extra options back into the database for future usage&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= CustomFields API =&lt;br /&gt;
&lt;br /&gt;
== Introduction to CustomFields API ==&lt;br /&gt;
&lt;br /&gt;
This chapter will cover the basics on how to manually and concretely use the CustomFields class to create/update/fetch/manage custom fields.&lt;br /&gt;
&lt;br /&gt;
This will give you a better understanding of how CustomFields works, as well as giving you insights on how to use it for your own applications.&lt;br /&gt;
&lt;br /&gt;
Important note: contrary to the Facade API, the core CustomFields class use columns names without any prefix, so don&#039;t try to use the prefix, it won&#039;t work! For example: if your field is named &#039;mycustomfield&#039; and prefix is &#039;cf_&#039;, when you use the Facade API you should use &#039;$object-&amp;gt;$path-&amp;gt;cf_mycustomfield&#039; but when you use the core CustomFields class directly, $fieldname = &#039;mycustomfield&#039;.&lt;br /&gt;
&lt;br /&gt;
== Prerequisite to use the CustomFields class ==&lt;br /&gt;
&lt;br /&gt;
Before using the CustomFields class, you first must instanciate it. This is a necessary preliminary step before using any of the methods that will be covered inside this chapter, and thus will not be repeated, only shown once here.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Include the CustomFields class (which contains the main API)&lt;br /&gt;
require_once(dirname(__FILE__).&#039;/../class/customfields.class.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Instanciate a CustomFields object&lt;br /&gt;
global $db; // the database handler created by Dolibarr&lt;br /&gt;
$table_element = &#039;currentmodule&#039;; // where $table_element is the database&#039;s table name for the module, without the Dolibarr prefix (eg: for &#039;llx_facture&#039;, table_element is &#039;facture&#039;). You can also find it inside the $object properties (just print_r() it).&lt;br /&gt;
$customfields = new CustomFields($db, $table_element); // instanciate the CustomFields class inside $customfields object&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
CustomFields keeps track of errors that can happen when executing methods and SQL queries inside the &#039;&#039;&#039;error&#039;&#039;&#039; and &#039;&#039;&#039;errors&#039;&#039;&#039; properties, which you can print with the printErrors() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Do something with CustomFields before&lt;br /&gt;
&lt;br /&gt;
if (count($customfields-&amp;gt;errors) &amp;gt; 0) { // If there is at least one error&lt;br /&gt;
    $customfields-&amp;gt;printErrors(); // print errors&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// You can also print your own error message, in two manners:&lt;br /&gt;
&lt;br /&gt;
// 1- submit your own message&lt;br /&gt;
$customfields-&amp;gt;printErrors(&#039;Your own error message here&#039;); // this will print ONLY your error message, using the Dolibarr&#039;s AJAX error dialog (standard way of printing errors)&lt;br /&gt;
&lt;br /&gt;
// 2- append your message in the customfields errors&lt;br /&gt;
$customfields-&amp;gt;errors[] = &#039;Your own error message to be appended with other CustomFields messages&#039;;&lt;br /&gt;
$customfields-&amp;gt;printErrors(); // Here your error message will be appended, thus all error messages will be printed. This allows you for example to add more intelligible informations on a specific error.&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== General purpose SQL methods ==&lt;br /&gt;
&lt;br /&gt;
CustomFields provide an interface to easily execute various common SQL requests, as well as your own custom SQL requests.&lt;br /&gt;
&lt;br /&gt;
* fetchAllTables() fetch a list of all the tables in the database&lt;br /&gt;
* fetchPrimaryField($table) fetch the column_name (and only the column_name!) of the primary field of a table. You can then use fetchFieldStruct()  with the column_name to get any information you want.&lt;br /&gt;
* fetchAny($columns, $table, $where=&#039;&#039;, $orderby=&#039;&#039;, $limitby=&#039;&#039;) allows you to issue any simple SQL query command, for example fetchAny(&#039;*&#039;, &#039;sometable&#039;) is equivalent to SELECT * FROM sometable; (but then everything is managed automatically and result is returned to you). NOTE: this method does NOT cache the database results, so that you always get the latest database state (whereas executeSQL() does cache queries to get faster responses).&lt;br /&gt;
* executeSQL($sql, $eventname) to execute any SQL query you want, this will return you a resource (and NOT a processed nuplet like fetchAny!). $eventname can be anything, it&#039;s just for Dolibarr logging facility.&lt;br /&gt;
* executeMultiSQL($sql, $eventname) to execute multiple SQL queries&lt;br /&gt;
* fetchFieldStruct($id=null, $nohide=false, $table=null, $cachepath=null) allows you to fetch the field&#039;s SQL structure of not only custom fields, but of any database&#039;s column using the $table variable. Eg: fetchFieldStruct(&#039;mycolumn&#039;, true, &#039;mytable&#039;);&lt;br /&gt;
* probeTable($table) helps you to check if a table exists in the database&lt;br /&gt;
* fetch_object($resource) will return a PHP object from a $resource, similarly to mysql_fetch_object() but with two advantages: it works on several DBMS (not only mysql), and it always return lowercase properties names (else depending on the configuration of the DBMS, the request can return uppercase or mixed case properties names).&lt;br /&gt;
* escape() and reverse_escape() are similare to mysql_real_escape but is reversible and DBMS-independent (so you can use it with other DBMSes).&lt;br /&gt;
&lt;br /&gt;
== Managing CustomFields structure ==&lt;br /&gt;
&lt;br /&gt;
The first step into CustomFields is to create and manage the structure of a custom field.&lt;br /&gt;
&lt;br /&gt;
=== Creating a custom field definition ===&lt;br /&gt;
&lt;br /&gt;
To create a custom field, you can use the addCustomField() method.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;addCustomField($fieldname, $type, $size, $nulloption, $defaultvalue = null, $constraint = null, $customtype = null, $customdef = null, $customsql = null, $fieldid = null, $extra = null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will take care of forging the (complicated) SQL query with the forgeSQLCustomField() method, and will return you an error if there was a problem.&lt;br /&gt;
&lt;br /&gt;
=== Updating a custom field definition ===&lt;br /&gt;
&lt;br /&gt;
To update a custom field, you can use the updateCustomField() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;&lt;br /&gt;
updateCustomField($fieldid, $fieldname, $type, $size, $nulloption, $defaultvalue, $constraint = null, $customtype = null, $customdef = null, $customsql = null, $extra = null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: the updateCustomField() and addCustomField() methods can both be used since updateCustomField() is in fact an alias for addCustomField(). CustomFields will automatically detect if a custom field already exists, and update it if that&#039;s true, or create it.&lt;br /&gt;
&lt;br /&gt;
Note2: updating a custom field will automatically take care of the constraints and foreign keys on its own, thus you can change the foreign key, or remove it altogether if set to null.&lt;br /&gt;
&lt;br /&gt;
=== Delete a custom field definition ===&lt;br /&gt;
&lt;br /&gt;
You can easily delete a custom field by column_name or by ordinal_position:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;deleteCustomField($id);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: constraints and foreign keys will also be deleted automatically so you shouldn&#039;t encounter any error.&lt;br /&gt;
&lt;br /&gt;
=== Fetch one custom field&#039;s SQL structure ===&lt;br /&gt;
&lt;br /&gt;
To get the SQL structure of a custom field, which can be quite useful to know for example its datatype or if it has a foreign key, or accepts null values, you can use the fetchFieldStruct() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
fields = $customfields-&amp;gt;fetchFieldStruct($id=null, $nohide=false, $table=null, $cachepath=null); // where $id can be a column_name, an ordinal_position from information_schema.columns or null if you want to fetch all the columns at once&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method will either return null, either a single field object, either an array on field objects.&lt;br /&gt;
&lt;br /&gt;
Note: this function will use caching by default to accelerate future results, but you can disable it, or even change the default cache path. For more informations please read the comments in the source code.&lt;br /&gt;
&lt;br /&gt;
=== Fetch several custom fields&#039; SQL structures ===&lt;br /&gt;
&lt;br /&gt;
Use the fetchAllFieldsStruct() method, which is an alias for fetchFieldStruct():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;fetchAllFieldsStruct($nohide=false, $table=null, $cachepath=null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: this method will always return an array of fields objects, even if only one is found (for more consistency), and this is the main difference with fetchFieldStruct(null).&lt;br /&gt;
&lt;br /&gt;
== Managing custom fields records ==&lt;br /&gt;
&lt;br /&gt;
Now that you know how to create and manage the structure of custom fields, we can proceed on to managing the records.&lt;br /&gt;
&lt;br /&gt;
=== Creating a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Simply use the create() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;create($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
$object must be an object containing as its properties the customfields data.&lt;br /&gt;
&lt;br /&gt;
For example, if you have a field named mycustomfield, your object must have $object-&amp;gt;mycustomfields defined.&lt;br /&gt;
&lt;br /&gt;
This method will store in the database ALL the custom fields that were created/edited and with a valid value (eg: an empty value for a field that does not accept null value is invalid) AT ONCE! So you can modify all the columns of one record at once, but not several records at once.&lt;br /&gt;
&lt;br /&gt;
Concretely, this is very useful with Dolibarr, because modules tends to store everything inside a single object generally called $object, and CustomFields tries to hook onto $object, so that you can just pass $object as is to create() and it will save all the custom fields.&lt;br /&gt;
&lt;br /&gt;
But you can also create an empty object with $object = new stdClass(); and then manually fill the custom fields records with whatever you want, and then call the create() method, this will work as well!&lt;br /&gt;
&lt;br /&gt;
You can also just modify only one column of one record, by just specifying one custom field column_name in your $object. The other custom fields that are not specified won&#039;t be created/modified.&lt;br /&gt;
&lt;br /&gt;
=== Updating a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Use the update() method, which is an alias for the create() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;update($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As noted above, update() is an alias of the create() method, which means that CustomFields will try to detect by itself if a record needs to be updated or created.&lt;br /&gt;
&lt;br /&gt;
=== Deleting a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Use the delete() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;delete($id); // where $id is = to fk_moduleid column, NOT rowid&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fetching a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Just use the fetch() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$records = $customfields-&amp;gt;fetch($id=null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where $id can either be = fk_moduleid (NOT rowid), or null to fetch all the records at once.&lt;br /&gt;
&lt;br /&gt;
This method will either return null, a single record object, or an array of records objects.&lt;br /&gt;
&lt;br /&gt;
=== Fetching all custom fields&#039; records ===&lt;br /&gt;
&lt;br /&gt;
Use the fetchAll() method, which is an alias for fetch():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$records = $customfields-&amp;gt;fetchAll();&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: this method will always return an array of records object, for more consistency (and this is the main difference with fetch(null) ).&lt;br /&gt;
&lt;br /&gt;
=== Fetching referenced records of a constrained field ===&lt;br /&gt;
&lt;br /&gt;
A constrained custom field points to another database table, and a value for this constrained field is the rowid of a record in the referenced table.&lt;br /&gt;
&lt;br /&gt;
Thus, the principal purpose of a constrained custom field is not the value of the field in itself (it&#039;s simply an integer), but rather the other fields available from the referenced table, the constrained field being in fact just a pointer.&lt;br /&gt;
&lt;br /&gt;
To fetch the remote record from the referenced table, use the fetchReferencedValuesList() of the CustomFields class:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$fkrecord = fetchReferencedValuesList($field, $id=null, $where=null, $allcolumns=false);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
&lt;br /&gt;
* $field is a custom field object (fetched using fetchFieldStruct() or fetchAllFieldsStruct()),&lt;br /&gt;
* $id is the rowid of the record to get, just like fetch() and fetchAll(). Can be null to fetch all remote records (or to use the $where clause).&lt;br /&gt;
* $where is a string without the WHERE clause (eg: &amp;quot;fk_facture=1&amp;quot;) to get finer control. In this case, you can use $id or set $id=null depending on your purpose. This is mainly a private argument to allow for cascade option.&lt;br /&gt;
* $allcolumns if false, it will fetch only the required columns to do the smart value substitution. If true, it will fetch all remote columns (useful for ODT substitution). If an array of strings, each string will be a column to fetch from the remote database.&lt;br /&gt;
&lt;br /&gt;
Note that this function is NOT recursive (it will fetch the records from the referenced table, and that&#039;s all, it won&#039;t fetch the custom fields of the referenced table nor recursively fetch the remote constrained custom fields). To do recursive fetching, see in the Facade API.&lt;br /&gt;
&lt;br /&gt;
Note2: this function automatically manages Smart Value Substitution depending of the $field-&amp;gt;column_name.&lt;br /&gt;
&lt;br /&gt;
=== Recursive fetching referenced records of a constrained field ===&lt;br /&gt;
&lt;br /&gt;
If you want to recursively fetch a constrained field (eg: constrainedfield1-&amp;gt;ref_table-&amp;gt;constrainedfield2-&amp;gt;ref_table2-&amp;gt;...), you need another function called fetchReferencedValuesRec(). Technically, this function is in the Facade API (inside /customfields/lib/customfields_aux.lib.php), but it isn&#039;t meant to be used by lambda users (contrary to customfields_fill_object()), but is mainly a helper function for the rest of the facade API.&lt;br /&gt;
&lt;br /&gt;
fetchReferencedValuesRec() works very similarly to fetchReferencedValuesList(), but it will work recursively.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$fkrecord = fetchReferencedValuesRec($customfields, $field, $id, $recursive=true)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where $customfields is the CustomFields instance for the module you are working on, $field is the constrained field you want to fetch, $id is the record&#039;s id you want to fetch, and $recursive enables or disables the recursion.&lt;br /&gt;
&lt;br /&gt;
Note that this function automatically manages Smart Value Substitution (and also recursively).&lt;br /&gt;
&lt;br /&gt;
Note2: This function tries to automatically avoid an infinite recursion loop by using a $blacklist: for a given constrained custom field, a table won&#039;t be visited twice. This means that for example you can do: invoice-&amp;gt;user-&amp;gt;socpeople-&amp;gt;invoice, this will work and every referenced fields will be fetched, but it will automatically stop the recursion at the second invoice, because else it could continue in a loop to user, socpeople, invoice, user, socpeople, etc.&lt;br /&gt;
&lt;br /&gt;
Note3: when adding recursively referenced fields, the key in the returned array will be prefixed by the name of the custom field that led to those remote fields. Thus, you can have something like &amp;quot;cf_myfield1_cf_remotefield1_cf_remotefield2&amp;quot; if you have a constrained field cf_myfield1-&amp;gt;cf_remotefield1-&amp;gt;cf_remotefield2.&lt;br /&gt;
&lt;br /&gt;
== Input forms for custom fields ==&lt;br /&gt;
&lt;br /&gt;
The API provides some neat ways to automatically manage the input forms for users to input new values, with the best adapted HTML form based on the custom field&#039;s data type.&lt;br /&gt;
&lt;br /&gt;
The following methods will print input fields that are best adapted to the SQL data type of your custom fields as well as figure out the possible values and even format them (for example: a not null enum will be printed as a HTML select list without an empty option; fetch automatically remote columns for constrained fields and show them in a select).&lt;br /&gt;
&lt;br /&gt;
So you don&#039;t have to bother how your data will be presented to the user nor how it will be processed.&lt;br /&gt;
&lt;br /&gt;
=== Printing an input form adapted to the field&#039;s data type ===&lt;br /&gt;
&lt;br /&gt;
showInputField() is a sort of converter customfieldstructure -&amp;gt; HTML input field.&lt;br /&gt;
&lt;br /&gt;
Use the &#039;&#039;&#039;showInputField()&#039;&#039;&#039; method to print the right input form for a custom field.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;showInputField($field, $currentvalue=null, $moreparam=&#039;&#039;, $ajax_php_callback=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
showInputField() takes 4 parameters:&lt;br /&gt;
&lt;br /&gt;
* $field is a CustomFields structure object (eg: returned by fetchFieldStruct()) and is necessary to detect the right datatype and the possible values.&lt;br /&gt;
* $currentvalue is the current value if you want to preselect a value (for example if the user already defined a value, you can reload it here). This is optional, in case it is not defined, either a null/empty value will be chosen, or either the default value defined when creating the custom field.&lt;br /&gt;
* $moreparam is used to add more HTML attributes, if you want for example to add some css styling or whatever you want.&lt;br /&gt;
* $ajax_php_callback is optional and allows to specify the relative url to the php callback script. If specified, an AJAX script will be automatically attached to this field (see AJAX-PHP callback below).&lt;br /&gt;
&lt;br /&gt;
showInputField() only prints ONE HTML input field, thus you have to enclose it/them by your own HTML form.&lt;br /&gt;
&lt;br /&gt;
eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print &#039;&amp;lt;form method=&amp;quot;get&amp;quot; action=&amp;quot;?&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
print &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;customtoken&amp;quot; value=&amp;quot;ABCDEFGH&amp;quot; /&amp;gt;&#039;; // example of a custom hidden HTML input field, that can be used to pass a token for example&lt;br /&gt;
// Loop over each custom fields (you have to load them before with $customfields-&amp;gt;fetchAllFieldsStruct()&lt;br /&gt;
foreach ($customfields-&amp;gt;fields as $field) {&lt;br /&gt;
    print $customfields-&amp;gt;showInputField($field, $currentvalue=null, $moreparam=&#039;&#039;);&lt;br /&gt;
}&lt;br /&gt;
print &#039;&amp;lt;/form&amp;gt;&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can also store the resulting HTML input field and process it by a regexp for example:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$myhtmlfield = $customfields-&amp;gt;showInputField($field, $currentvalue=null, $moreparam=&#039;&#039;);&lt;br /&gt;
preg_replace($myhtmlfield, ..., ...);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: for constrained fields, if the name follows the convention of &#039;&#039;&#039;Smart Value Substitution&#039;&#039;&#039; (ie: remote columns names are included in the custom field&#039;s name), showInputField() will &#039;&#039;&#039;automatically fetch the remote values, format them, and present them&#039;&#039;&#039; in a HTML select.&lt;br /&gt;
&lt;br /&gt;
=== Printing an input form for a custom field ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;showInputForm()&#039;&#039;&#039; is a function used to print a single (and only one!) custom field with an adapted HTML input field, but enclosed by a HTML form. This is used for example when you want to allow to edit a single custom field but not the others.&lt;br /&gt;
&lt;br /&gt;
showInputForm() is in fact a wrapper for showInputField(), thus it will call showInputField() and simply enclose it by a form.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;showInputForm($id, $field, $currentvalue=null, $idvar=&#039;id&#039;, $page=null, $moreparam=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== AJAX-PHP callback ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;showInputFieldAjax()&#039;&#039;&#039; will output an AJAX script (using jQuery) to send and receive data to and from a specified php script. The AJAX script will automatically manage HTML form input&#039;s updating as long as the received data follows some convention.&lt;br /&gt;
&lt;br /&gt;
An AJAX script can be attached to any field (including non-CustomFields) by using the method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;showInputFieldAjax($id, $php_callback_url, $on_func=&amp;quot;change&amp;quot;, $request_type=&amp;quot;post&amp;quot;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
* $id is the HTML id of the field you want to attach the AJAX to.&lt;br /&gt;
* $phpcallback the relative path from Dolibarr&#039;s htdocs root folder to the php script that will receive and send back data.&lt;br /&gt;
* $on_func is the Javascript or jQuery event that should trigger the AJAX script (by default on change).&lt;br /&gt;
* $request_type is the HTML query type that will be used to send the data to the PHP callback (either &amp;quot;get&amp;quot; or &amp;quot;post&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
The AJAX script will automatically manage sending/receiving of data:&lt;br /&gt;
&lt;br /&gt;
* automatically send all form&#039;s inputs values, as well as the current calling field&#039;s name and value. The sending happens on change by default, but another event can be set using the $on_event argument. Data will be sent using standard GET or POST.&lt;br /&gt;
* Upon reception of data from the php script, the AJAX script will automatically parse the data and update the HTML fields.&lt;br /&gt;
&lt;br /&gt;
The PHP callback script must send back to AJAX the data in JSON encoded format, and with with a specific format:&lt;br /&gt;
&lt;br /&gt;
* the data must be an associative array, where each entry&#039;s key is the name (not ID!) of an HTML field to update.&lt;br /&gt;
* each entry&#039;s value is an associative array, where each sub-entry&#039;s key is the action to do. Available actions: &#039;options&#039;, &#039;value&#039;, &#039;html&#039;, &#039;alert&#039;.&lt;br /&gt;
* each sub-entry&#039;s value is the value for this action and HTML field.&lt;br /&gt;
&lt;br /&gt;
Thus, this approach allows to generically support a wide range of updating actions, managed automatically.&lt;br /&gt;
&lt;br /&gt;
You, the implementer, have just to edit the PHP callback script and return an associative array defining what action you want for each field you want to update. This is exactly what has been implemented in the Custom AJAX Functions library, which simplifies even more your job by managing automatically the step described here (generating AJAX and formatting data), so that you can focus on &#039;&#039;&#039;making the data&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Printing custom fields in documents ==&lt;br /&gt;
&lt;br /&gt;
The API can also automatically manage the printing and formatting of custom fields in your ODT, PDF and HTML documents.&lt;br /&gt;
&lt;br /&gt;
=== Printing in HTML ===&lt;br /&gt;
&lt;br /&gt;
You can either just print the value as-is, or pass them through the printField() or simpleprintField() function to format them (very useful for constrained fields) - see below.&lt;br /&gt;
&lt;br /&gt;
=== Printing in documents ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;printField()&#039;&#039;&#039; is used to properly print and translate a custom field&#039;s record&#039;s value. Generally, you can just print the custom field&#039;s value as-is, but in a few cases (eg for boolean types, or constrained fields), using the printField() method will more properly print the value.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;printField($field, $value, $outputlangs=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
* $field is the custom field&#039;s SQL structure as an object (eg: returned by fetchFieldStruct()).&lt;br /&gt;
* $value is the custom field&#039;s value for the current record (eg: $customfields-&amp;gt;fields-&amp;gt;myfield).&lt;br /&gt;
* $outputlangs is the Translate object for the target language.&lt;br /&gt;
&lt;br /&gt;
For example, with a constrained field with Smart Value Substitution, instead of printing the id like 1, it will print the full name of user like FirstName Name.&lt;br /&gt;
&lt;br /&gt;
This method will also translate a value if a translation is found in the language files.&lt;br /&gt;
&lt;br /&gt;
Another method &#039;&#039;&#039;printFieldPDF()&#039;&#039;&#039; can be used to properly print a value for a PDF (properly encoding the characters).&lt;br /&gt;
&lt;br /&gt;
Powertip: this function can also be used to get the Smart Value Substituted value of the field (instead of printing the rowid) in the field is constrained.&lt;br /&gt;
&lt;br /&gt;
=== Simple printing in documents ===&lt;br /&gt;
&lt;br /&gt;
If you don&#039;t want to have to manually fetch the structure of every custom field whose you want to print the value, you can then use a simplifier method called &#039;&#039;&#039;simpleprintField()&#039;&#039;&#039; which will take as input a custom field name instead of a custom field SQL structure object.&lt;br /&gt;
&lt;br /&gt;
This method is just a simplifier (Facade) wrapper for the printField() method, and thus will have the same advantages (fetch constrained fields, values translation, etc.).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;simpleprintField($fieldname, $value, $outputlangs=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
* $fieldname is the custom field&#039;s name (eg: &amp;quot;myfield&amp;quot;).&lt;br /&gt;
* $value is the custom field&#039;s value for the current record (eg: $customfields-&amp;gt;fields-&amp;gt;myfield).&lt;br /&gt;
* $outputlangs is the Translate object for the target language.&lt;br /&gt;
&lt;br /&gt;
Another method &#039;&#039;&#039;simpleprintFieldPDF()&#039;&#039;&#039; can be used to properly print a value for a PDF (properly encoding the characters).&lt;br /&gt;
&lt;br /&gt;
Powertip: this function can also be used to get the Smart Value Substituted value of the field (instead of printing the rowid) in the field is constrained.&lt;br /&gt;
&lt;br /&gt;
=== Find the label ===&lt;br /&gt;
&lt;br /&gt;
Finding the label of a custom field might not be as simple as it seems. First, values of properties objects may be easily used, but their name (key) is a worst job. Secondly, custom fields&#039; labels can be translated, and the following functions are expressly made for that purpose. Thirdly, for PDF and some other documents you have to encode the characters if you want them to properly be drawn.&lt;br /&gt;
&lt;br /&gt;
So if you want to print a custom field&#039;s label with translation (multilingual) support, just use the &#039;&#039;&#039;findLabel()&#039;&#039;&#039; method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$translatedfieldname = $customfields-&amp;gt;findLabel($fieldname, $outputlangs = &#039;&#039;); // where $fieldname is just a custom field label (eg: cf_mycustomfield or mycustomfield, both will work) and $outputlangs is the target language object (see the Translate class native of Dolibarr)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As for the other printing methods, you also have the &#039;&#039;&#039;findLabelPDF()&#039;&#039;&#039; wrapper method to easily print labels that with characters encoded for PDF.&lt;br /&gt;
&lt;br /&gt;
= Facade API =&lt;br /&gt;
&lt;br /&gt;
The Facade API was made to ease the usage of the CustomFields class with the Dolibarr system. It provides a neat way to do many operations with single commands, and will automatically manage the loading of the CustomFields class, and its usage with many optimizations.&lt;br /&gt;
&lt;br /&gt;
== customfields_fill_object() ==&lt;br /&gt;
&lt;br /&gt;
You can load the Facade API and then use the &#039;&#039;&#039;customfields_fill_object()&#039;&#039;&#039; to automatically fetch the fields structures and the records, and also automatically link to remote fields when a constrained field is found. This is an all-in-one solution to easily fetch CustomFields datas, and is also optimized to fully use caching and other special properties of the CustomFields class.&lt;br /&gt;
&lt;br /&gt;
Concretely, you just have to include the Facade API, then pass the customfields_fill_object() with a Dolibarr $object, and the Facade API will return a CustomFields object with appended the custom fields records and also the custom fields&#039; SQL structures.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$customfields = customfields_fill_object($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* $object will then include the custom fields records associated with this object (eg: $object-&amp;gt;customfields-&amp;gt;cf_yourfield).&lt;br /&gt;
* $customfields will be a CustomFields object containing the fields structures (eg: $customfields-&amp;gt;fields-&amp;gt;yourfield).&lt;br /&gt;
&lt;br /&gt;
You can also call any CustomFields function if you need to using the $customfields object, eg: $customfields-&amp;gt;simpleprintField($object-&amp;gt;customfields-&amp;gt;cf_yourfield);&lt;br /&gt;
&lt;br /&gt;
Note: $object can also be a dummy object containing only two properties that are required: $object-&amp;gt;id and $object-&amp;gt;table_element.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will now describe the full specification of this function:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$customfields = customfields_fill_object(&amp;amp;$object,$fromobject = null, $outputlangs = null, $prefix = null,$pdfformat = false);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* $object is the object where the customfields will be saved&lt;br /&gt;
* $fromobject is the object from where the customfields will be fetched (this can be a different module than $object, or can be null to be the same as $object)&lt;br /&gt;
* $outputlangs is the target language for translation of fields value (if empty, the default current global translation object will be used)&lt;br /&gt;
* $prefix allows you to save the custom fields in a sub-object. By default, when $prefix=null, custom fields are saved in the path $object-&amp;gt;customfields, but when you specify a prefix, custom fields will be saved inside $object-&amp;gt;customfields-&amp;gt;$prefix. This may be useful if you want to store multiple object&#039;s custom fields or records in the same $object.&lt;br /&gt;
* $pdfformat defines if you want a special formatting for the values: if null, there&#039;s no formatting, if false, there is a simple formatting (eg: translation of fields&#039; values), if true then the formatting will be adapted for PDF templating.&lt;br /&gt;
&lt;br /&gt;
For more informations on pratical usages of this function, please [[Module_CustomFields#Implementing_in_php_code_.28dolibarr_core_modules_or_your_own_module.29|read the user documentation on how to implement CustomFields in php code]] or [[Module_CustomFields_Cases#Linking_Dolibarr_objects_from_two_different_modules|read an example case of linking two objects of different modules together]].&lt;br /&gt;
&lt;br /&gt;
== customfields_fill_object_lines() ==&lt;br /&gt;
&lt;br /&gt;
Similar to customfields_fill_object() for normal custom fields, there is also a &#039;&#039;&#039;customfields_fill_object_lines()&#039;&#039;&#039; to fetch custom fields associated with lines of products/services. This function is also optimized and it is strongly advised to use it and to not try to fetch manually this kind of custom fields (ie: associated with lines of products/services rather than objects), because there are a lot of optimizations going on behind the scene and that are very hard to do manually.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields for lines of products/services (you can then access them by doing $object-&amp;gt;customfields-&amp;gt;lines-&amp;gt;$lineid-&amp;gt;cf_yourfield)&lt;br /&gt;
$customfields = customfields_fill_object_lines($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Requirements: $object can be a dummy object but must contain at least 2 properties: $object-&amp;gt;id and $object-&amp;gt;table_element_line.&lt;br /&gt;
&lt;br /&gt;
For more informations on pratical usages of this function, please [[Module_CustomFields#Implementing_in_php_code_.28dolibarr_core_modules_or_your_own_module.29|read the user documentation on how to implement CustomFields in php code]].&lt;br /&gt;
&lt;br /&gt;
= Architecture =&lt;br /&gt;
Here is a full list of the CustomFields packaged files with a short description (for a more in-depth view just crawl the source files, they are full of comments):&lt;br /&gt;
&lt;br /&gt;
== Core files ==&lt;br /&gt;
files that are necessary for the CustomFields to work, they contains the core functions&lt;br /&gt;
&lt;br /&gt;
* /customfields/admin/customfields.php --- Administrator&#039;s configuration panel : this is where you create and manage the custom fields definitions&lt;br /&gt;
* /customfields/class/actions_customfields.class.php --- Hooks class : used to hook into Dolibarr core modules without altering any core file (can be used to hook into your own modules too). Also used to preprocess the parameters before calling the printforms library.&lt;br /&gt;
* /customfields/class/customfields.class.php --- Core class : every database action is made here in this class. You can find some printing functions because they are very generic.&lt;br /&gt;
* /customfields/conf/conf_customfields.lib.php --- Configuration file : contains the main configurable variables to adapt CustomFields to your needs or to expand its support to other modules and more native sql types.&lt;br /&gt;
* /customfields/conf/conf_customfields_func.lib.php --- Auxiliary functions library to read and manage config variables&lt;br /&gt;
* /customfields/core/modules/modCustomFields.class.php --- Dolibarr&#039;s module definition file : this is a core file necessary for Dolibarr to recognize the module and to declare the hooks to Dolibarr (but it does not store anything else than meta-informations).&lt;br /&gt;
* /customfields/core/substitutions/functions_customfields.lib.php --- CustomFields substitution class for ODT generation : necessary to support customfields tags in ODT files&lt;br /&gt;
* /customfields/core/triggers/interface_50_modCustomFields_SaveFields.class.php --- Core triggers file : this is where the actions on records are managed. This is an interface between other modules and CustomFields management. You should not modify this file but you should add your triggers in $triggersarray inside conf_customfields.lib.php, but if you really have a special need and know what you do, you can edit this file to directly add your own triggers. Also, if CustomFields are shown in the forms but cannot be modified nor saved, then probably this trigger file is not detected by Dolibarr (maybe you can try to lower the number 50 to raise the priority?).&lt;br /&gt;
* /customfields/fields/customfields_fields_extend.lib.php.example --- Overloading functions library with users functions: this is where implementers can use their own php code to manage the custom fields they want (one function per action and per field).&lt;br /&gt;
* /customfields/fields/customfields_fields_ajax_custom.lib.php.example --- Custom AJAX functions library with users functions: this is where implementers can use their own php code to manage the custom fields cascading and AJAX calls, similarly to the Overloading functions library (one function per action and per field).&lt;br /&gt;
* /customfields/langs/code_CODE/customfields.lang --- Core language file : this is where you can translate the admin config panel (data types names, labels, descriptions, etc.)&lt;br /&gt;
* /customfields/langs/code_CODE/customfields-user.lang --- User defined language file : this is where you can store the labels and values of your custom fields (see the related chapter)&lt;br /&gt;
* /customfields/lib/customfields_aux.lib.php --- Simplifier library to easily use CustomFields class to populate an $object with custom fields datas (Facade design pattern). This is what you should use for most of your needs.&lt;br /&gt;
* /customfields/lib/customfields_printforms.lib.php --- Core printing library for records : contains only printing functions, there&#039;s not really any core functions inside but it allows to manage the printing of the custom fields records and their editing.&lt;br /&gt;
* /customfields/lib/customfields_ajax_wrapper.lib.php --- AJAX wrapper library to automatically manage cascading at objects creation. This isn&#039;t really necessary for CustomFields to function, this is more an addition to allow dynamical AJAX updating, but CustomFields core functionalities can work without this library.&lt;br /&gt;
* /customfields/sql/*.sql --- Unused (the tables are created directly via a function in the customfields.class.php) - but the sql files are still valid, they were used as squeletton in the first designs.&lt;br /&gt;
&lt;br /&gt;
== PDFTest module ==&lt;br /&gt;
optional module to append a page listing all custom fields values on newly generated PDF documents. To be used to test if custom fields are working correctly before making one&#039;s own template.&lt;br /&gt;
&lt;br /&gt;
* /customfieldspdftest/class/actions_customfieldspdftest.class.php --- Main functions library and hooks class, to hook onto pdf generation. Contains the PDFTest functions.&lt;br /&gt;
* /customfieldspdftest/core/modules/modCustomFieldsPDFTest.class.php --- Module descriptor for Dolibarr&lt;br /&gt;
&lt;br /&gt;
== Runtime outline ==&lt;br /&gt;
&lt;br /&gt;
Here is an outline of how CustomFields works: what functions will be called and why:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At admin page:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* No external function call (apart for some icons or needed admin functions like for tabs), everything that is done are sql queries via CustomFields class (to fetch and modify the sql columns in the customfields tables).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At printing:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Module&#039;s hook formObjectOptions calls Dolibarr&#039;s hookmanager&lt;br /&gt;
* Hookmanager calls CustomFields /class/actions_customfields.class.php, function formObjectOptions() of CustomFields&lt;br /&gt;
* Preprocess a few parameters (mainly the $action) and calls customfields_print_forms()&lt;br /&gt;
* customfields_print_forms() preprocess all the hook&#039;s parameters and assimilate them with the CustomFields&#039;s configuration parameters (there&#039;s no connection to the database at this stage, only parameters preprocessing), then calls /lib/customfields_printforms.lib.php, functions customfields_print_creation_form() or customfields_print_datasheet_form&lt;br /&gt;
* both customfields_print_creation_form() lookup the database using CustomFields class and prints empty custom fields; customfields_print_datasheet_form() lookup the database using CustomFields class, fetch the values of the custom fields and prints them;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At edition:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Same steps as &#039;&#039;&#039;At printing&#039;&#039;&#039;&lt;br /&gt;
* then customfields_print_datasheet_form() process the custom field being edited differently than the others: if editing, it prints the html input field (to enter a new value), or if saving it just saves the data into the database by calling the CUSTOMFIELDS_MODIFY trigger and then continue to print the other fields normally.&lt;br /&gt;
* CUSTOMFIELDS_MODIFY trigger calls $customfields-&amp;gt;update($newrecord);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At creation (saving):&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Module&#039;s creation trigger MYMODULE_CREATE calls Dolibarr&#039;s run_trigger()&lt;br /&gt;
* Dolibarr run_trigger() calls CustomFields trigger file /core/trigger/interface_50_modCustomFields_SaveFields.class.php, function run_trigger()&lt;br /&gt;
* CustomFields&#039;s run_trigger() assimilate the given parameters with the CustomFields&#039;s config parameters, and if the trigger is found in $triggersarray, then the appropriate action is taken (by default: _CREATE or another standard action if recognized), if not in $triggersarray, the function will still try to automatically detect the appropriate action by detecting MYMODULE_ALISTOFACTIONS with a regular expression.&lt;br /&gt;
* The appropriate action is then done (creation in this case) by calling $customfields-&amp;gt;create($record).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At edition or creation of product lines:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Similar to &#039;&#039;&#039;At edition&#039;&#039;&#039; but with hooks: formCreateProductOptions and formEditProductOptions.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At ODT document generation and substitution:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Module&#039;s ODT generator calls for Dolibarr&#039;s function complete_substitution_array(), which then calls for CustomFields substitution file /core/substitution/functions_customfields.lib.php, function customfields_completesubstitutionarray()&lt;br /&gt;
* CustomFields&#039;s function customfields_completesubstitutionarray() calls the simplifier library customfields_aux.lib.php, function customfields_fill_object() and make the substitution inside the $substitutionarray.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At ODT document generation and substitution for product lines:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Same as the previous point but this function is called for every product lines, and thus the data is cached. Also the simplifier function is here called customfields_fill_object_lines(), and the CustomFields&#039;s substitution function called customfields_completesubstitutionarray_lines().&lt;br /&gt;
&lt;br /&gt;
= How to add the support of a new module =&lt;br /&gt;
&lt;br /&gt;
There are two cases: either the module already implements hooks, and you just have to configure CustomFields to support it, or either the module does not implements hooks nor triggers.&lt;br /&gt;
&lt;br /&gt;
This chapter will guide you through the steps to make any module supported by CustomFields (be it a Dolibarr core module or your own module).&lt;br /&gt;
&lt;br /&gt;
== Preliminary test ==&lt;br /&gt;
&lt;br /&gt;
This preliminary test will allow you to know at a glance how much work needs to be done in order to make your module supported by CustomFields, and it will also give you all the config parameters if they are available.&lt;br /&gt;
&lt;br /&gt;
Important note: this test will only give data about hooks, which is the main part to make CustomFields work for your module. But for triggers, there is no way to test, so you will have to check the triggers manually (either by testing if the custom fields are saved at creation page, or by looking at the source code of the objects classes).&lt;br /&gt;
&lt;br /&gt;
Edit the php script of the module (check the URL when you access the module&#039;s datasheet or creation page - attention both are two different pages, so you have to do this twice), then put this at the very &#039;&#039;&#039;end&#039;&#039;&#039; of the php script:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// CustomFields debuglines to show config parameters for current module&lt;br /&gt;
print(&amp;quot;&amp;lt;pre&amp;gt;&amp;quot;); // html nice output&lt;br /&gt;
print(&amp;quot;table_element: {$object-&amp;gt;table_element} - table_element_line: {$object-&amp;gt;table_element_line} - contexts: &amp;quot;);&lt;br /&gt;
print_r($hookmanager-&amp;gt;contextarray);&lt;br /&gt;
print(&amp;quot; - conf hooks_modules: &amp;quot;);&lt;br /&gt;
print_r($conf-&amp;gt;hooks_modules);&lt;br /&gt;
print(&amp;quot; - hooks attached: &amp;quot;);&lt;br /&gt;
print_r($hookmanager-&amp;gt;hooks);&lt;br /&gt;
print(&amp;quot; - user rights: &amp;quot;);&lt;br /&gt;
print_r($user-&amp;gt;rights);&lt;br /&gt;
print(&amp;quot;&amp;lt;/pre&amp;gt;&amp;quot;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now reload the page, and scroll at the very bottom. You should see some informations about the module.&lt;br /&gt;
&lt;br /&gt;
Depending on the data available, you will know what you have to do:&lt;br /&gt;
&lt;br /&gt;
* If all the datas is available, then it&#039;s ok, jump to [[Module_CustomFields_Dev#Configuring_CustomFields_to_support_the_module]] and copy these datas to the parameters of conf_customfields.lib.php&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;table_element&#039;&#039;&#039;: $object is not defined, try to find how is called the main object variable and replace $object by this variable in the debug code given above (eg: in Agenda, the object variable is called $agenda and $agendacomm).&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;table_element_line&#039;&#039;&#039;: either there&#039;s no products lines for this module, either the main object variable is not called $object (see the previous point). One last case is that the object is not standard and does not store table_element_line (the name of the table that stores its products lines), in which case you have to manually add it in the class (eg: $this-&amp;gt;table_element_line = &#039;something&#039;).&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;contexts&#039;&#039;&#039;: the hookmanager is not defined, and probably there&#039;s no hook at all in this module, so you&#039;ll have to implement them by yourself. Jump to [[Module_CustomFields_Dev#Hooks]].&lt;br /&gt;
&lt;br /&gt;
* If there is a list of &#039;&#039;&#039;contexts&#039;&#039;&#039; but the module cannot be found in the list: the hookmanager is defined but the module has not defined a context, you still have to implement the hooks. Jump to [[Module_CustomFields_Dev#Hooks]].&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;conf hooks_modules&#039;&#039;&#039;: this variable is not necessary, empty or not it doesn&#039;t matter. But if you see customfields and/or customfieldspdftest inside the list, it is a confirmation that CustomFields indeed is hooking the module correctly.&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;hooks attached&#039;&#039;&#039;, or CustomFields cannot be found in the list: CustomFields has not been properly configured to hook to this module, or you forgot to disable then re enable CustomFields in order to update the CustomFields&#039;s hooks. Jump to [[Module_CustomFields_Dev#Configuring_CustomFields_to_support_the_module]].&lt;br /&gt;
&lt;br /&gt;
* If there is no &#039;&#039;&#039;$user-&amp;gt;rights&#039;&#039;&#039;: there is either no standard security implemented in this module, or either the variable is called with another name. If there is no security, you don&#039;t need to configure it in CustomFields, but if there is a security, you need to find how the variable is called (like $object can get another name sometimes).&lt;br /&gt;
&lt;br /&gt;
* If there is &#039;&#039;&#039;$user-&amp;gt;rights&#039;&#039;&#039; but the module can&#039;t be found in the list: either the name for the rights is different (this often happens, the rights array is not yet standardized), or either the rights are hardcoded into the module&#039;s code (rights should be announced to the $user-&amp;gt;rights array, but sometimes it happens that the dev forgot to do so, and thus the right is entirely hardcoded and you can&#039;t find it in the dynamic rights array). In any of these two cases, you will have to manually read the module&#039;s code to find the right. Also, maybe there is no security implemented, but since $user-&amp;gt;rights is declared this is less probable.&lt;br /&gt;
&lt;br /&gt;
Here is an example of output on a correctly hooked module (invoice):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
table_element: facture - table_element_line: facturedet - contexts: Array&lt;br /&gt;
(&lt;br /&gt;
    [0] =&amp;gt; searchform&lt;br /&gt;
    [1] =&amp;gt; leftblock&lt;br /&gt;
    [2] =&amp;gt; toprightmenu&lt;br /&gt;
    [3] =&amp;gt; invoicecard&lt;br /&gt;
)&lt;br /&gt;
 - conf hooks_modules: Array&lt;br /&gt;
(&lt;br /&gt;
    [customfields] =&amp;gt; invoicecard:propalcard:productcard:ordercard:thirdpartycard:contactcard:ordersuppliercard:invoicesuppliercard:membercard:actioncard:projectcard:projecttaskcard:contractcard:interventioncard:doncard:tripsandexpensescard:taxvatcard:expeditioncard:invoicecard:propalcard:ordercard:ordersuppliercard:invoicesuppliercard&lt;br /&gt;
    [customfieldspdftest] =&amp;gt; Array&lt;br /&gt;
        (&lt;br /&gt;
            [0] =&amp;gt; pdfgeneration&lt;br /&gt;
            [1] =&amp;gt; ordersuppliercard&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
)&lt;br /&gt;
 - hooks attached: Array&lt;br /&gt;
(&lt;br /&gt;
    [invoicecard] =&amp;gt; Array&lt;br /&gt;
        (&lt;br /&gt;
            [customfields] =&amp;gt; ActionsCustomFields Object&lt;br /&gt;
                (&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
)&lt;br /&gt;
 - user rights: stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [user] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [user] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [lire] =&amp;gt; 1&lt;br /&gt;
                    [creer] =&amp;gt; 1&lt;br /&gt;
                    [password] =&amp;gt; 1&lt;br /&gt;
                    [supprimer] =&amp;gt; 1&lt;br /&gt;
                    [export] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [self] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [creer] =&amp;gt; 1&lt;br /&gt;
                    [password] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [user_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [readperms] =&amp;gt; 1&lt;br /&gt;
                    [write] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [self_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [readperms] =&amp;gt; 1&lt;br /&gt;
                    [writeperms] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [group_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [read] =&amp;gt; 1&lt;br /&gt;
                    [readperms] =&amp;gt; 1&lt;br /&gt;
                    [write] =&amp;gt; 1&lt;br /&gt;
                    [delete] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    [facture] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [lire] =&amp;gt; 1&lt;br /&gt;
            [creer] =&amp;gt; 1&lt;br /&gt;
            [invoice_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [unvalidate] =&amp;gt; 1&lt;br /&gt;
                    [send] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [valider] =&amp;gt; 1&lt;br /&gt;
            [paiement] =&amp;gt; 1&lt;br /&gt;
            [supprimer] =&amp;gt; 1&lt;br /&gt;
            [facture] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [export] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    [propale] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [lire] =&amp;gt; 1&lt;br /&gt;
            [creer] =&amp;gt; 1&lt;br /&gt;
            [valider] =&amp;gt; 1&lt;br /&gt;
            [propal_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [send] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [cloturer] =&amp;gt; 1&lt;br /&gt;
            [supprimer] =&amp;gt; 1&lt;br /&gt;
            [export] =&amp;gt; 1&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    [produit] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [lire] =&amp;gt; 1&lt;br /&gt;
            [creer] =&amp;gt; 1&lt;br /&gt;
            [supprimer] =&amp;gt; 1&lt;br /&gt;
            [export] =&amp;gt; 1&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    etc... a lot of other rights...&lt;br /&gt;
&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When you are finished with implementing the support of CustomFields in the module, you should delete these debug lines.&lt;br /&gt;
&lt;br /&gt;
== Implementing the support of CustomFields in the module ==&lt;br /&gt;
&lt;br /&gt;
If there is a module you want to implement the support of CustomFields, but does not yet has the required components, here is a guide on how to implement these components.&lt;br /&gt;
&lt;br /&gt;
=== Hooks ===&lt;br /&gt;
&lt;br /&gt;
Implementing hooks will allow custom fields to appear on your module&#039;s page. They will also be editable and they will save the data. For creation, you need to use a trigger (see the subchapter below).&lt;br /&gt;
&lt;br /&gt;
First you should be familiar with the way that hooks are implemented in modules. If you don&#039;t, please first [[Hooks_system#Implementation|read the hooks documentation]].&lt;br /&gt;
&lt;br /&gt;
An example case of implementation of hooks into a Dolibarr core file can be found in the [[Module_CustomFields_Cases|CustomFields Example Cases wiki page]].&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Datasheet hook&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
To implement CustomFields in your module, you must initialize the hook manager, then use the following hook context:&lt;br /&gt;
* formObjectOptions&lt;br /&gt;
&lt;br /&gt;
This hook must be placed everywhere in your code where you want custom fields to appear (eg: on the creation page and datasheet view page).&lt;br /&gt;
&lt;br /&gt;
Note: Generally, you will have to place the hook just before the end of a table tag (&#039;&#039;&#039;&amp;lt;/table&amp;gt;&#039;&#039;&#039;), so if you don&#039;t know where to place it (eg: if you&#039;re implementing CustomFields on a Dolibarr core module), just search for this tag in the page shown in the url (eg: /htdocs/commande/fiche.php).&lt;br /&gt;
&lt;br /&gt;
Eg: First load the hookmanager at the beginning of your php script:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Initialize technical object to manage hooks of thirdparties. Note that conf-&amp;gt;hooks_modules contains array array&lt;br /&gt;
include_once(DOL_DOCUMENT_ROOT.&#039;/core/class/hookmanager.class.php&#039;);&lt;br /&gt;
$hookmanager=new HookManager($db);&lt;br /&gt;
$hookmanager-&amp;gt;initHooks(array(&#039;yourmodule&#039;));&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then place the hook as many times as required:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// ... a lot of php code ...&lt;br /&gt;
&lt;br /&gt;
print &#039;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&#039;; // end of the last row of a table&lt;br /&gt;
&lt;br /&gt;
// Other attributes&lt;br /&gt;
$parameters=array(&#039;colspan&#039; =&amp;gt; &#039; colspan=&amp;quot;3&amp;quot;&#039;); // specifying the colspan is optional but will help CustomFields know how to correctly print the table&lt;br /&gt;
$reshook=$hookmanager-&amp;gt;executeHooks(&#039;formObjectOptions&#039;,$parameters,$object,$action);    // Note that $action and $object may have been modified by hook&lt;br /&gt;
&lt;br /&gt;
print &#039;&amp;lt;/table&amp;gt;&#039;; // end of the table, this is what you&#039;re looking for, place the hook just above&lt;br /&gt;
&lt;br /&gt;
// ... a lot of php code ... redo the same if you find another &amp;lt;/table&amp;gt; and there&#039;s a page where there&#039;s no custom fields but you want to place custom fields on there&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note2: If after implementing the hook you receive an error or custom fields don&#039;t appear, you may have to change $object to another variable, depending on what is called the main object (eg: in Agenda it&#039;s called $action and $actioncomm).&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Lines hooks&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
For product lines, you should anyway use the product lines template files: htdocs/core/tpl/freeproductline_edit.tpl.php&lt;br /&gt;
&lt;br /&gt;
If you don&#039;t, you can still manually implement the required hooks, what is required is that you supply $line (which is the current line object being processed) in the $parameters array:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$parameters=array(&#039;line&#039;=&amp;gt;$line);&lt;br /&gt;
&lt;br /&gt;
echo $hookmanager-&amp;gt;executeHooks(&#039;formEditProductOptions&#039;,$parameters,$object,$action);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The two important things here are that you supply:&lt;br /&gt;
* $line: the line object, with a $line-&amp;gt;rowid property.&lt;br /&gt;
* $object: the parent object of the line, with a $object-&amp;gt;rowid property. For example, if $line is an invoice line, then $object must be the parent invoice object. This is necessary so that CustomFields can automatically recognize the link between the twos.&lt;br /&gt;
&lt;br /&gt;
Possible hooks names:&lt;br /&gt;
* formEditProductOptions: called when you edit a line.&lt;br /&gt;
* formCreateProductOptions: called when you create a line. $line is not required here, because we create a new product line! you can leave $parameters=array(); empty.&lt;br /&gt;
Note: there&#039;s no hook for viewing, because showing the customfields will clutter the visual field, but if you really want you can also do it by adding a formViewProductOptions hook.&lt;br /&gt;
&lt;br /&gt;
=== Triggers ===&lt;br /&gt;
&lt;br /&gt;
First you need to know how to implement triggers in Dolibarr. For more informations about how to implement triggers in your module, please read the [[Triggers#Manage_and_create_a_new_trigger.27s_action|triggers wiki page]].&lt;br /&gt;
&lt;br /&gt;
An example case of implementation of hooks into a Dolibarr core file can be found in the [[Module_CustomFields_Cases|CustomFields Example Cases wiki page]].&lt;br /&gt;
&lt;br /&gt;
Implementing triggers on your module will allow CustomFields to save the fields at creation (datasheet edition is already managed by hooks).&lt;br /&gt;
&lt;br /&gt;
You need to implement 3 triggers:&lt;br /&gt;
- on creation of object - eg: YOURMODULE_CREATE&lt;br /&gt;
- on creation of lines - eg: YOURMODULELINE_INSERT (optional, only if your module has product lines)&lt;br /&gt;
- on update of lines - eg: YOURMODULELINE_UPDATE (optional, only if your module has product lines)&lt;br /&gt;
&lt;br /&gt;
Note: you need to supply the sql id of the newly created object before calling the trigger.&lt;br /&gt;
&lt;br /&gt;
Eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$this-&amp;gt;id = $this-&amp;gt;db-&amp;gt;last_insert_id(MAIN_DB_PREFIX.&amp;quot;yourmodule_table_element&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
// Appel des triggers&lt;br /&gt;
include_once(DOL_DOCUMENT_ROOT . &amp;quot;/core/class/interfaces.class.php&amp;quot;);&lt;br /&gt;
$interface=new Interfaces($this-&amp;gt;db);&lt;br /&gt;
$result=$interface-&amp;gt;run_triggers(&#039;YOURMODULE_CREATE&#039;,$this,$user,$langs,$conf);&lt;br /&gt;
if ($result &amp;lt; 0) {&lt;br /&gt;
    $error++; $this-&amp;gt;errors=$interface-&amp;gt;errors;&lt;br /&gt;
}&lt;br /&gt;
// Fin appel triggers&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note2: if it&#039;s a Dolibarr core module, you can easily find the right place to put the trigger by searching for &#039;create sql&#039; in the class subfolder (eg: URL is /htdocs/commande/fiche.php, the class is in /htdocs/commande/class/commande.class.php).&lt;br /&gt;
&lt;br /&gt;
Note3: CustomFields also supports a few other triggers like _CLONE, _UPDATE, _MODIFY, but they are not required and may not work depending on how your module works to manage these actions.&lt;br /&gt;
&lt;br /&gt;
== Configuring CustomFields to support the module ==&lt;br /&gt;
&lt;br /&gt;
=== Preliminary work ===&lt;br /&gt;
&lt;br /&gt;
You need to get the parameters of this module, and you will then put them in the CustomFields configuration file (conf_customfields.lib.php).&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Getting the parameters automatically&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
You can use the [[Module_CustomFields_Dev#Preliminary_test|preliminary test]] to easily get all those parameters.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Getting the parameters manually&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
To configure CustomFields to support a module, you need at least 5 parameters:&lt;br /&gt;
* the module&#039;s table_name (name of the sql table where the module stores its datas)&lt;br /&gt;
* the module&#039;s context (name given by the module to the hookmanager)&lt;br /&gt;
* the module&#039;s idvar (name of the variable that holds the sql row id of the record)&lt;br /&gt;
* the module&#039;s trigger action(s) (triggers on creation and update, see the related chapter above)&lt;br /&gt;
* the module&#039;s rights (for security purposes)&lt;br /&gt;
&lt;br /&gt;
To get these parameters manually, you can take a look inside the code of the module&#039;s files you want to implement:&lt;br /&gt;
* module&#039;s table name: either find it by yourself in the database (looks like llx_themodulename) or by printing it in actions_customfields.class.php by adding &#039;&#039;&#039;print_r($parameters)&#039;&#039;&#039; and search for $parameters-&amp;gt;table_element&lt;br /&gt;
* module&#039;s context: search for &amp;quot;callHooks(&amp;quot; without the quotes or take a look at the wiki: http://wiki.dolibarr.org/index.php/Hooks_system&lt;br /&gt;
or you can get it by printing it in the /htdocs/customfields/class/actions_customfields.class.php by adding print_r($parameters) and search for $parameters-&amp;gt;context&lt;br /&gt;
* module&#039;s trigger actions: search for &amp;quot;run_triggers(&amp;quot; or take a look at the wiki: http://wiki.dolibarr.org/index.php/Triggers#List_of_known_triggers_actions&lt;br /&gt;
* module&#039;s idvar: look at the url when you access one of the module&#039;s record, and you should see something like fiche.php?xxxid=12 - you need the xxxid (eg: socid, facid, etc.).&lt;br /&gt;
* module&#039;s security: in the page shown in URL when accessing the module&#039;s datasheet, edit this file and add &#039;&#039;&#039;print($user-&amp;gt;rights)&#039;&#039;&#039;, then in the list try to find the correct right (generally something like $user-&amp;gt;modulename-&amp;gt;creer). You can also try to search for $user-&amp;gt;rights in the sourcecode of this module.&lt;br /&gt;
&lt;br /&gt;
With these values, edit the config file (/htdocs/customfields/conf/conf_customfields.lib.php), particularly the $modulesarray and $triggersarray variables:&lt;br /&gt;
&lt;br /&gt;
=== Configure hooks and main parameters ===&lt;br /&gt;
&lt;br /&gt;
Add every parameters except triggers in the $modulesarray:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$modulesarray = array(&lt;br /&gt;
                      array(&#039;context&#039;=&amp;gt;&#039;invoicecard&#039;, &#039;table_element&#039;=&amp;gt;&#039;facture&#039;, &#039;idvar&#039;=&amp;gt;&#039;facid&#039;, &#039;rights&#039;=&amp;gt;array(&#039;$user-&amp;gt;rights-&amp;gt;facture-&amp;gt;creer&#039;)), // Client Invoice&lt;br /&gt;
                      array(&#039;context&#039;=&amp;gt;&#039;membercard&#039;, &#039;table_element&#039;=&amp;gt;&#039;adherent&#039;, &#039;idvar&#039;=&amp;gt;&#039;rowid&#039;, &#039;rights&#039;=&amp;gt;array(&#039;$user-&amp;gt;rights-&amp;gt;adherent-&amp;gt;creer&#039;), &#039;tabs&#039;=&amp;gt;array(&#039;objecttype&#039;=&amp;gt;&#039;member_admin&#039;, &#039;function&#039;=&amp;gt;&#039;member_admin_prepare_head&#039;, &#039;lib&#039;=&amp;gt;DOL_DOCUMENT_ROOT.&#039;/core/lib/member.lib.php&#039;)), // Members / Adherents&lt;br /&gt;
                     ); // Edit me to add the support of another module - NOTE: Lowercase only!&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are also a lot of others parameters you can give to CustomFields so that the module gets better managed or to add a few other functionnalities, here is a non-exhaustive list:&lt;br /&gt;
* context: name of the module&#039;s context.&lt;br /&gt;
* table_element: name of the sql table that holds the object&#039;s datas.&lt;br /&gt;
* idvar: name of the variable that holds the id of the object in the sql database (by default &#039;id&#039; or &#039;rowid&#039;).&lt;br /&gt;
* rights: prevent the custom fields to be editable unless all the rights set here are enabled (preventing custom fields to be viewed is useless because they won&#039;t show anyway if the page can&#039;t be loaded due to insufficient rights, so this is directly handled by the module).&lt;br /&gt;
* tabs=&amp;gt;array(objecttype, function, lib, tabname, tabtitle): used to embed CustomFields&#039;s admin panel into another module&#039;s admin page.&lt;br /&gt;
&lt;br /&gt;
=== Configure creation triggers ===&lt;br /&gt;
&lt;br /&gt;
Add the triggers in $triggersarray:&lt;br /&gt;
$triggersarray = array(&amp;quot;order_create&amp;quot;=&amp;gt;&amp;quot;commande&amp;quot;,&lt;br /&gt;
                       &amp;quot;yourmodule_triggeraction1&amp;quot;=&amp;gt;&amp;quot;yourmodule_tablename&amp;quot;,&lt;br /&gt;
                       &amp;quot;yourmodule_triggeraction2&amp;quot;=&amp;gt;&amp;quot;yourmodule_tablename&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
Note: generally you will be looking to implement the _CREATE.&lt;br /&gt;
&lt;br /&gt;
Note2: if a triggeraction is not recognized by CustomFields (eg: mymodule_addpayment) then by default CustomFields will assimilate it to a _create action (it will create and save the values for the custom fields, just for a module&#039;s creation page).&lt;br /&gt;
&lt;br /&gt;
Note3: CustomFields also supports a few other triggers like _CLONE, _UPDATE, _MODIFY, but they are not required and may not work depending on how your module works to manage these actions.&lt;br /&gt;
&lt;br /&gt;
=== Configuration troubleshooting ===&lt;br /&gt;
&lt;br /&gt;
* If you cannot see the custom fields appear on your module&#039;s page, this means the hooks are not implemented or configured in CustomFields, please read the related chapters.&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet but there&#039;s no edit button icon: the module implements security but you did not configure it properly in CustomFields, please find the right security in $user-&amp;gt;rights and set it in conf_customfields.lib.php.&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the creation page but are not saved: you forgot to set the trigger in $triggersarray, or the creation trigger does not exist for this module and you have to implement it. Jump to [[Module_CustomFields_Dev#Triggers]].&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet but not on the creation page (or vice versa): A hook is missing on the page where the custom fields do not appear. Jump to [[Module_CustomFields_Dev#Hooks]].&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet with the edit button, but when the edit button is clicked, everything disappear: either the $idvar is not properly set (look at the URL to know if you need to set idvar), or the hook is not properly made ($object is not defined and it&#039;s called by another name, but the hook use $object instead of the correct variable).&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet with the edit button, can be edited but cannot be saved (the data is not saved): same as the previous point: either $idvar or $object bug.&lt;br /&gt;
&lt;br /&gt;
Also you might find useful to &#039;&#039;&#039;[[Module_CustomFields#General_advices_for_troubleshooting|enable php errors]]&#039;&#039;&#039; to better track errors if that&#039;s not already done.&lt;br /&gt;
&lt;br /&gt;
= Example cases =&lt;br /&gt;
&lt;br /&gt;
You can find a compilation of concrete, practical examples on the [[Module_CustomFields_Cases]] wiki page.&lt;br /&gt;
&lt;br /&gt;
= TO DO =&lt;br /&gt;
&lt;br /&gt;
You will find below the public list of features that should be implemented in the future.&lt;br /&gt;
&lt;br /&gt;
If you have an idea about a new feature that isn&#039;t listed below, feel free to add it to the list!&lt;br /&gt;
&lt;br /&gt;
== To Do ==&lt;br /&gt;
&lt;br /&gt;
* Better management of hidden fields with custom cascading (eg: works great on creation form, but on edit form and creation form with edit action, if the parents already have a value, the hidden fields will stay hidden! This is because of the AJAX is not called, and this is normal). -&amp;gt; Propose a better custom cascade function storing the hidden field&#039;s state inside extraoptions?&lt;br /&gt;
&lt;br /&gt;
== To Document ==&lt;br /&gt;
* Automatic recopy on cloning for custom fields and products&#039; lines custom fields.&lt;br /&gt;
* new API function: customfields_clone_or_recopy($object, $fromobject, $action2 = null)&lt;br /&gt;
&lt;br /&gt;
== Should do ==&lt;br /&gt;
* Add the possibility to mark a custom field to be shown in tables listings (eg: as a column in products listings) with &amp;quot;order by this column&amp;quot; buttons and stuffs. Will be difficult if the hooks are not yet implemented in listings.&lt;br /&gt;
* Checkbox (multiple checkboxes, one for each possible value) and multiselect (big select and can use SHIFT to multiselect) (store in XML file) (but impossible with constrained fields, can&#039;t store multiple foreign keys ids in one single sql field, but would work for other types like enum)&lt;br /&gt;
use fieldset, input checkbox and label for, eg:&lt;br /&gt;
    &amp;lt;fieldset&amp;gt;&amp;lt;legend&amp;gt;&amp;lt;label for=&amp;quot;cf_productslist&amp;quot;&amp;gt; $langs-&amp;gt;trans(&#039;cf_productslist&#039;)&amp;lt;/label&amp;gt;&amp;lt;/legend&amp;gt;&lt;br /&gt;
    &amp;lt;input type=&amp;quot;checkbox&amp;quot; name=&amp;quot;cf_productslist_choice1&amp;quot; value=&amp;quot;1&amp;quot; id=&amp;quot;cf_productslist_choice1&amp;quot; /&amp;gt;&amp;lt;label for=&amp;quot;cf_productslist_choice1&amp;quot;&amp;gt; $langs-&amp;gt;trans(&#039;cf_productslist_choice1&#039;)&amp;lt;/label&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
    &amp;lt;input type=&amp;quot;checkbox&amp;quot; name=&amp;quot;cf_productslist_choice2&amp;quot; value=&amp;quot;1&amp;quot; id=&amp;quot;cf_productslist_choice2&amp;quot; /&amp;gt;&amp;lt;label for=&amp;quot;cf_productslist_choice2&amp;quot;&amp;gt; $langs-&amp;gt;trans(&#039;cf_productslist_choice2&#039;)&amp;lt;/label&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
    etc...&lt;br /&gt;
    &amp;lt;/fieldset&amp;gt;&lt;br /&gt;
* Add a javascript options values generator for the enum type (a hidden input that would be shown only when DropdownBox type is selected and that would permit to add options by clicking a plus button).&lt;br /&gt;
* Replace triggers in customfields class by hookmanager?&lt;br /&gt;
* AJAX previsualisation of the HTML custom field that will be created.&lt;br /&gt;
* In ODTs, create special tags to directly access day, month or year value of a date type field independently of the rest.&lt;br /&gt;
* New overloading function&#039;s action: &amp;quot;get&amp;quot;, similar to view except that it works not only on Dolibarr&#039;s view but whenever someone tries to access this field&#039;s value.&lt;br /&gt;
* Add Upload and Image field types (this would allow for an easier integration inside ODT and PDF templates than the current workflow of Dolibarr).&lt;br /&gt;
&lt;br /&gt;
== Known bugs ==&lt;br /&gt;
* in product and service modules, if you edit a field, the proposals and other fields below won&#039;t be shown, you need to refresh the page. This problem resides in Dolibarr I think (since we are simply using a hook).&lt;br /&gt;
* ficheinter (Intervention module) custom fields will stay in create mode just after create page is submitted (this is because of the $action that is not standard - $action=edit or something).&lt;br /&gt;
&lt;br /&gt;
== Never/Maybe one day ==&lt;br /&gt;
* Add support for repeatable (predefined) invoices (the way it is currently managed makes it very difficult to manage this without making a big exception, adding specific functions in customfields modules that would not at all will be reusable anywhere else, when customfields has been designed to be as generic as possible to support any module and any version of dolibarr, because it&#039;s managed by a totally different table while it&#039;s still managed by the same module, CustomFields work with the paradigm: one module, one table).&lt;br /&gt;
* Add an AJAX select box for constrained values to automatically select the appropriate name for Smart Value Substitution : when a constrained type is selected and a table is selected, a hidden select box would show up with the list of the fields of this table to choose the values that will be printed as the values for this customfield (eg: for table llx_users you could select the &amp;quot;nom&amp;quot; field and then it would automatically prepend &amp;quot;nom_&amp;quot; to the field&#039;s name).&lt;br /&gt;
* Fine-grained rights management: rights per group, per user, and per field.&lt;br /&gt;
* Replace overloading functions (extend class) by hookmanager if possible (but how to simulate the *full switch? Plus this will force users to make their own modules to make those functions, and to disable/renable their modules everytime they will add a new function...).&lt;br /&gt;
* Refactor the trigger array: merge it with the modulesarray (along with a new way to specify which customfields trigger action a trigger should correspond, eg: &#039;linebill_insert&#039;=&amp;gt;&#039;customfields_create&#039;). But the triggers aren&#039;t necessarily associated with a specific module...&lt;br /&gt;
* AJAX autocompletion / auto population of field&#039;s content when typing the beginning of a value in a text box.&lt;/div&gt;</summary>
		<author><name>Lrq3000</name></author>
	</entry>
	<entry>
		<id>https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Dev&amp;diff=34399</id>
		<title>Module CustomFields Dev</title>
		<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Dev&amp;diff=34399"/>
		<updated>2015-10-06T22:27:09Z</updated>

		<summary type="html">&lt;p&gt;Lrq3000: /* Lines hooks */ Fixed the explanation&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:CustomFields]]&lt;br /&gt;
{{TemplateDocDevEn}}&lt;br /&gt;
{{TemplateModEN}}&lt;br /&gt;
{{BasculeDevUserEn|&lt;br /&gt;
name=CustomFields|&lt;br /&gt;
num=8500|&lt;br /&gt;
devdoc=This page|&lt;br /&gt;
userdoc=[[Module_CustomFields]]|}}&lt;br /&gt;
[[Category:FAQ EN]]&lt;br /&gt;
&lt;br /&gt;
These notes are aimed to developpers who want to extend the functionnalities of the module or want to use it for extreme cases.&lt;br /&gt;
This is not meant to guide implementers on how to implement and configure the module (see the related chapter for that).&lt;br /&gt;
&lt;br /&gt;
If you are an implementer and just want to use CustomFields with its normal functions, you can read the [[Module_CustomFields|user documentation of CustomFields]].&lt;br /&gt;
&lt;br /&gt;
You can also jump directly to [[Module_CustomFields_Cases]] wiki page for a collection of practical examples.&lt;br /&gt;
&lt;br /&gt;
= Principles =&lt;br /&gt;
&lt;br /&gt;
CustomFields is simply a wrapper for SQL databases.&lt;br /&gt;
&lt;br /&gt;
The module is based on SQL to manage the fields and most of the stuff. It uses and understands standard SQL.&lt;br /&gt;
&lt;br /&gt;
The main principles of CustomFields are:&lt;br /&gt;
&lt;br /&gt;
* one SQL column = one Dolibarr field.&lt;br /&gt;
* one SQL type = one method to show and edit the field in Dolibarr.&lt;br /&gt;
* almost everything should be configurable from the configuration file, or admin interface.&lt;br /&gt;
* be independent from Dolibarr&#039;s data structures.&lt;br /&gt;
&lt;br /&gt;
CustomFields sits besides Dolibarr, and does not mix with it, even if in the Dolibarr&#039;s interface it looks like CustomFields is embedded inside Dolibarr, in reality, CustomFields is totally independent.&lt;br /&gt;
&lt;br /&gt;
= Goals =&lt;br /&gt;
&lt;br /&gt;
This module was made to provide an easy way to manage custom sql fields, not only for users and implementers, but also for developpers to use in their own module:&lt;br /&gt;
* by easily adding the support of custom fields for your own module by using this module;&lt;br /&gt;
* by reusing custom fields from another module in your own module;&lt;br /&gt;
* by managing sql fields in your own module by using the CustomFields class (which will take care of everything from printing, editing to PDF/ODT templating).&lt;br /&gt;
&lt;br /&gt;
So this module is not only an auxiliary of other modules, but it can also be the core part of your own module by using it as your SQL query class for fields.&lt;br /&gt;
&lt;br /&gt;
= SQL structure =&lt;br /&gt;
&lt;br /&gt;
A CustomFields table needs to respect three parameters that never change:&lt;br /&gt;
&lt;br /&gt;
* table&#039;s name (format: llx_*dolibarrmodule*_customfields where *dolibarrmodule* = table_element in conf_customfields.lib.php)&lt;br /&gt;
* first column: rowid, which is the primary key for each field&lt;br /&gt;
* second column: fk_*dolibarrmodule* which is the foreign key that links every customfields record to the Dolibarr&#039;s module that is associated (invoice, order, etc...).&lt;br /&gt;
&lt;br /&gt;
Apart from the table&#039;s name and the first two special columns, every other columns are just standard sql columns that you can freely create, edit or delete as you want, from the module&#039;s administration page or from a third-party SQL management tool (such as phpMyAdmin).&lt;br /&gt;
&lt;br /&gt;
You can also create more complex sql fields: constraints, foreign keys, transformations, other sql types, etc...).&lt;br /&gt;
&lt;br /&gt;
Then the module will try to manage the sql fields you manually created the best it can:&lt;br /&gt;
* by printing appropriate input fields in the Dolibarr gui interface.&lt;br /&gt;
* by providing the developper an easy access to these fields in php (eg: $customfields-&amp;gt;cf_myfield or $object-&amp;gt;customfields-&amp;gt;cf_myfield) for any use, including PDF templates and ODT templates, or in any php script (so that you can use these fields in your own module).&lt;br /&gt;
&lt;br /&gt;
= SQL Indexes =&lt;br /&gt;
&lt;br /&gt;
The module automatically adds an index on the primary key and foreign key (first and second columns).&lt;br /&gt;
&lt;br /&gt;
To further optimize the performances of your database, and particularly the customfields tables, you may want to manually add indexes based on usage.&lt;br /&gt;
&lt;br /&gt;
Also, you may add indexes on remote tables used by constrained fields, which could optimize even more the performances since constrained fields have the worst performance drawback for CustomFields.&lt;br /&gt;
&lt;br /&gt;
= SQL Compatibility mode =&lt;br /&gt;
&lt;br /&gt;
The module relies on referential integrity checks and foreign keys of your DBMS to keep the database clean and to provide the constrained fields feature. Referential integrity checks is part of the SQL standards and should be used whenever possible, which always was a main goal for CustomFields (to be respectful of SQL standards).&lt;br /&gt;
&lt;br /&gt;
Unluckily, a few DBMS systems still don&#039;t support this critical functionality of the SQL standard, for example MySQL enables MyISAM engine by default, which doesn&#039;t support this feature.&lt;br /&gt;
&lt;br /&gt;
In these cases, you can either do two things:&lt;br /&gt;
* Switch to an engine which support referential integrity. For example with MySQL, you can enable InnoDB, which support referential integrity checks and foreign keys.&lt;br /&gt;
* Use CustomFields compatibility mode (more infos below).&lt;br /&gt;
&lt;br /&gt;
In the case your database does not support referential integrity checks, CustomFields will automatically switch to the SQL compatibility mode, which will allow you to use CustomFields even on databases where foreign keys are not possible.&lt;br /&gt;
&lt;br /&gt;
In compatibility mode, you will get nearly all CustomFields advanced features such as Constrained fields, but the customfields&#039; tables in the database won&#039;t be cleaned automatically (no delete on cascade, meaning that when you delete an object, the linked custom fields will still be kept in your database), and constrained fields won&#039;t be updated automatically on update.&lt;br /&gt;
&lt;br /&gt;
Nevertheless, these missing cleanup functionalities won&#039;t be seen by your users because they will never be accessible: on deletion of an object, the custom fields won&#039;t ever be accessible again from Dolibarr&#039;s interface, and object&#039;s ids are never changed since Dolibarr also relies on this. Thus in compatibility mode, CustomFields will act just like normally, but your database will be a bit less clean.&lt;br /&gt;
&lt;br /&gt;
= JSON extension for extra options =&lt;br /&gt;
&lt;br /&gt;
== Introduction to JSON in CustomFields ==&lt;br /&gt;
The relational model is very great to store and fetch data with a declarative language, and this is very powerful, but this model also imposes a few limitations, for example the relations must be well formatted and predefined.&lt;br /&gt;
&lt;br /&gt;
That&#039;s why we finally implemented a &#039;&#039;&#039;JSON extension&#039;&#039;&#039; to manage &#039;&#039;&#039;extra options&#039;&#039;&#039; and associate them with custom fields. This allows to add many properties (or columns if you are more used to the relational model) on-the-fly without having to predefine them prior to storing and using them.&lt;br /&gt;
&lt;br /&gt;
This allows to add an undefined number of columns/properties/extra options that will help manage special behaviours for custom fields. For example, it is currently used to allow to change the order of appearance of custom fields, and to store and manage remote table and columns datas for constrained fields on DBMS that do not support Foreign Keys checks.&lt;br /&gt;
&lt;br /&gt;
We chose JSON over XML because the library are simpler and the data footprint smaller, and because CustomFields does not need advanced features of XML like XQuery, data typing and meta structures (at least not for now).&lt;br /&gt;
&lt;br /&gt;
The other advantage is that php objects can directly be encoded into JSON without any preprocessing, and can then as straightly be decoded from JSON back into a php object.&lt;br /&gt;
&lt;br /&gt;
A simple mean to encode and decode back an object into and from a storage place, as well as allow users to easily add/remove extra options were the main requirement, and JSON (+ JSON libraries for PHP) meet this criteria.&lt;br /&gt;
&lt;br /&gt;
However, note that the extra options are now encoded as an associative array, so as to avoid any ambiguities (there&#039;s no associative array in JSON, they are converted to objects).&lt;br /&gt;
&lt;br /&gt;
== Implementation of JSON ==&lt;br /&gt;
The implementation is very simple: a table called &#039;&#039;&#039;llx_customfields_extraoptions&#039;&#039;&#039; is created when you enable the CustomFields module, and this table only contains three columns:&lt;br /&gt;
* table_name (varchar)&lt;br /&gt;
* column_name (varchar)&lt;br /&gt;
* extraoptions (blob)&lt;br /&gt;
&lt;br /&gt;
(table_name, column_name) both forms the primary key of the table, and links to columns (custom fields) of other customfields tables.&lt;br /&gt;
extraoptions is a blob, which allows to store the data as binary (as-is) without encoding (which solves a big problem of JSON which is encoding special and foreign characters, here there is no problem at all).&lt;br /&gt;
&lt;br /&gt;
When fetching custom fields, the module simply JOIN the extraoptions table, which adds an &#039;&#039;&#039;extraoptions&#039;&#039;&#039; column for each custom field in the SQL result. Then it is quite easy to just json_decode() the data returned inside the extraoptions column.&lt;br /&gt;
&lt;br /&gt;
This methodology allows to have all the advantages of a relational database, with the flexibility of a free-schema model:&lt;br /&gt;
* everything is stored inside the database (only one storage place)&lt;br /&gt;
* fixed columns allows for relational joining and optimization&lt;br /&gt;
* thus queries (fetching) are optimised&lt;br /&gt;
* at the same time, the extraoptions are automatically retrieved and associated with every custom field, and whenever needed they can be used&lt;br /&gt;
* and thus we also get a flexible model to &#039;&#039;&#039;add extra (virtual) columns whenever needed&#039;&#039;&#039; without having to alter the relational database schema&lt;br /&gt;
&lt;br /&gt;
Thus no critical information is and should be stored inside the extraoptions column because they would not be query-able, only extra options that are just used to tweak the management of those fields inside the PHP application should be stored. But this is a wonderful way to add non critical informations that still allows to develop more complex features and management for special custom fields.&lt;br /&gt;
&lt;br /&gt;
== CustomFields JSON API ==&lt;br /&gt;
&lt;br /&gt;
=== Storing extra options ===&lt;br /&gt;
&lt;br /&gt;
You can use the &#039;&#039;&#039;setExtra()&#039;&#039;&#039; method to set extra options of a custom field.&lt;br /&gt;
&lt;br /&gt;
Eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$extra = array(); // $extra should always be an associative array, it&#039;s no longer an object&lt;br /&gt;
$extra[&#039;myproperty&#039;] = &#039;Anything I want here&#039;; // set a property/extra option like this&lt;br /&gt;
$extra[&#039;category&#039;][&#039;subcategory&#039;][&#039;prop&#039;] = true; // we can set recursive properties, there&#039;s not any limit to how deep the hierarchy can be&lt;br /&gt;
&lt;br /&gt;
require_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomField($db, &#039;currentmodule&#039;);&lt;br /&gt;
$fieldid = &#039;mycustomfields&#039;; // $fieldid can either be a column_name or an ordinal_position from information_schema.columns&lt;br /&gt;
$result = $customfields-&amp;gt;setExtra($fieldid, $extra); // store the extra options inside the database&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can also use addCustomField() or updateCustomField() with an $extra array, and these functions will also call the setExtra() method.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: the extra options you set are &#039;&#039;&#039;appended&#039;&#039;&#039; to the ones already stored in the database. However, you can also overwrite previous options, just use the same name of property but with a different value (the replacement is recursive, you can do it at any level in the hierarchy).&lt;br /&gt;
&lt;br /&gt;
=== Fetching extra options ===&lt;br /&gt;
&lt;br /&gt;
Extra options are automatically fetched along with a custom field structure, decoded, and stored inside the extra sub-property of any field object, or CustomFields object.&lt;br /&gt;
&lt;br /&gt;
When fetched, extra options are stored as a sub-object for the current field object (which itself is stored as a subobject of the CustomFields object if caching is enabled).&lt;br /&gt;
&lt;br /&gt;
Eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
require_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomField($db, &#039;currentmodule&#039;);&lt;br /&gt;
$fields = fetchAllFieldsStruct(); // fetching fields structure will automatically fetch extra options at the same time. This works with fetchAllFieldsStruct() and fetchFieldStruct()&lt;br /&gt;
&lt;br /&gt;
// Now that you fetched the fields structure, you can just access the extra options sub-properties:&lt;br /&gt;
print_r($fields-&amp;gt;mycustomfield-&amp;gt;extra); // will print all extra options for the field mycustomfield&lt;br /&gt;
print_r($customfields-&amp;gt;fields-&amp;gt;mycustomfield-&amp;gt;extra); // you can also access the same field(s) directly from the CustomFields object if caching is enabled (default behaviour)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Modifying extra options ===&lt;br /&gt;
&lt;br /&gt;
You can just combine the two previous points to edit extra options of a custom field: just load a custom field, edit on-the-fly the extra property with PHP code, and then store it back directly into the database:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
require_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomField($db, &#039;currentmodule&#039;);&lt;br /&gt;
$fields = fetchAllFieldsStruct();&lt;br /&gt;
&lt;br /&gt;
$fields-&amp;gt;mycustomfield-&amp;gt;extra[&#039;newproperty&#039;] = &#039;This is a new property&#039;; // add a new property over the old extra options sub-object&lt;br /&gt;
$fields-&amp;gt;mycustomfield-&amp;gt;extra[&#039;mycategory&#039;] = &#039;Overwrite the old category&#039;; // this will replace the previous value of &#039;mycategory&#039; extra option&lt;br /&gt;
&lt;br /&gt;
$customfields-&amp;gt;setExtra(&#039;mycustomfield&#039;, $fields-&amp;gt;mycustomfield-&amp;gt;extra); // store the modified extra options back into the database for future usage&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= CustomFields API =&lt;br /&gt;
&lt;br /&gt;
== Introduction to CustomFields API ==&lt;br /&gt;
&lt;br /&gt;
This chapter will cover the basics on how to manually and concretely use the CustomFields class to create/update/fetch/manage custom fields.&lt;br /&gt;
&lt;br /&gt;
This will give you a better understanding of how CustomFields works, as well as giving you insights on how to use it for your own applications.&lt;br /&gt;
&lt;br /&gt;
Important note: contrary to the Facade API, the core CustomFields class use columns names without any prefix, so don&#039;t try to use the prefix, it won&#039;t work! For example: if your field is named &#039;mycustomfield&#039; and prefix is &#039;cf_&#039;, when you use the Facade API you should use &#039;$object-&amp;gt;$path-&amp;gt;cf_mycustomfield&#039; but when you use the core CustomFields class directly, $fieldname = &#039;mycustomfield&#039;.&lt;br /&gt;
&lt;br /&gt;
== Prerequisite to use the CustomFields class ==&lt;br /&gt;
&lt;br /&gt;
Before using the CustomFields class, you first must instanciate it. This is a necessary preliminary step before using any of the methods that will be covered inside this chapter, and thus will not be repeated, only shown once here.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Include the CustomFields class (which contains the main API)&lt;br /&gt;
require_once(dirname(__FILE__).&#039;/../class/customfields.class.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Instanciate a CustomFields object&lt;br /&gt;
global $db; // the database handler created by Dolibarr&lt;br /&gt;
$table_element = &#039;currentmodule&#039;; // where $table_element is the database&#039;s table name for the module, without the Dolibarr prefix (eg: for &#039;llx_facture&#039;, table_element is &#039;facture&#039;). You can also find it inside the $object properties (just print_r() it).&lt;br /&gt;
$customfields = new CustomFields($db, $table_element); // instanciate the CustomFields class inside $customfields object&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
CustomFields keeps track of errors that can happen when executing methods and SQL queries inside the &#039;&#039;&#039;error&#039;&#039;&#039; and &#039;&#039;&#039;errors&#039;&#039;&#039; properties, which you can print with the printErrors() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Do something with CustomFields before&lt;br /&gt;
&lt;br /&gt;
if (count($customfields-&amp;gt;errors) &amp;gt; 0) { // If there is at least one error&lt;br /&gt;
    $customfields-&amp;gt;printErrors(); // print errors&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// You can also print your own error message, in two manners:&lt;br /&gt;
&lt;br /&gt;
// 1- submit your own message&lt;br /&gt;
$customfields-&amp;gt;printErrors(&#039;Your own error message here&#039;); // this will print ONLY your error message, using the Dolibarr&#039;s AJAX error dialog (standard way of printing errors)&lt;br /&gt;
&lt;br /&gt;
// 2- append your message in the customfields errors&lt;br /&gt;
$customfields-&amp;gt;errors[] = &#039;Your own error message to be appended with other CustomFields messages&#039;;&lt;br /&gt;
$customfields-&amp;gt;printErrors(); // Here your error message will be appended, thus all error messages will be printed. This allows you for example to add more intelligible informations on a specific error.&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== General purpose SQL methods ==&lt;br /&gt;
&lt;br /&gt;
CustomFields provide an interface to easily execute various common SQL requests, as well as your own custom SQL requests.&lt;br /&gt;
&lt;br /&gt;
* fetchAllTables() fetch a list of all the tables in the database&lt;br /&gt;
* fetchPrimaryField($table) fetch the column_name (and only the column_name!) of the primary field of a table. You can then use fetchFieldStruct()  with the column_name to get any information you want.&lt;br /&gt;
* fetchAny($columns, $table, $where=&#039;&#039;, $orderby=&#039;&#039;, $limitby=&#039;&#039;) allows you to issue any simple SQL query command, for example fetchAny(&#039;*&#039;, &#039;sometable&#039;) is equivalent to SELECT * FROM sometable; (but then everything is managed automatically and result is returned to you)&lt;br /&gt;
* executeSQL($sql, $eventname) to execute any SQL query you want, this will return you a resource (and NOT a processed nuplet like fetchAny!). $eventname can be anything, it&#039;s just for Dolibarr logging facility.&lt;br /&gt;
* executeMultiSQL($sql, $eventname) to execute multiple SQL queries&lt;br /&gt;
* fetchFieldStruct($id=null, $nohide=false, $table=null, $cachepath=null) allows you to fetch the field&#039;s SQL structure of not only custom fields, but of any database&#039;s column using the $table variable. Eg: fetchFieldStruct(&#039;mycolumn&#039;, true, &#039;mytable&#039;);&lt;br /&gt;
* probeTable($table) helps you to check if a table exists in the database&lt;br /&gt;
* fetch_object($resource) will return a PHP object from a $resource, similarly to mysql_fetch_object() but with two advantages: it works on several DBMS (not only mysql), and it always return lowercase properties names (else depending on the configuration of the DBMS, the request can return uppercase or mixed case properties names).&lt;br /&gt;
* escape() and reverse_escape() are similare to mysql_real_escape but is reversible and DBMS-independent (so you can use it with other DBMSes).&lt;br /&gt;
&lt;br /&gt;
== Managing CustomFields structure ==&lt;br /&gt;
&lt;br /&gt;
The first step into CustomFields is to create and manage the structure of a custom field.&lt;br /&gt;
&lt;br /&gt;
=== Creating a custom field definition ===&lt;br /&gt;
&lt;br /&gt;
To create a custom field, you can use the addCustomField() method.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;addCustomField($fieldname, $type, $size, $nulloption, $defaultvalue = null, $constraint = null, $customtype = null, $customdef = null, $customsql = null, $fieldid = null, $extra = null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will take care of forging the (complicated) SQL query with the forgeSQLCustomField() method, and will return you an error if there was a problem.&lt;br /&gt;
&lt;br /&gt;
=== Updating a custom field definition ===&lt;br /&gt;
&lt;br /&gt;
To update a custom field, you can use the updateCustomField() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;&lt;br /&gt;
updateCustomField($fieldid, $fieldname, $type, $size, $nulloption, $defaultvalue, $constraint = null, $customtype = null, $customdef = null, $customsql = null, $extra = null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: the updateCustomField() and addCustomField() methods can both be used since updateCustomField() is in fact an alias for addCustomField(). CustomFields will automatically detect if a custom field already exists, and update it if that&#039;s true, or create it.&lt;br /&gt;
&lt;br /&gt;
Note2: updating a custom field will automatically take care of the constraints and foreign keys on its own, thus you can change the foreign key, or remove it altogether if set to null.&lt;br /&gt;
&lt;br /&gt;
=== Delete a custom field definition ===&lt;br /&gt;
&lt;br /&gt;
You can easily delete a custom field by column_name or by ordinal_position:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;deleteCustomField($id);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: constraints and foreign keys will also be deleted automatically so you shouldn&#039;t encounter any error.&lt;br /&gt;
&lt;br /&gt;
=== Fetch one custom field&#039;s SQL structure ===&lt;br /&gt;
&lt;br /&gt;
To get the SQL structure of a custom field, which can be quite useful to know for example its datatype or if it has a foreign key, or accepts null values, you can use the fetchFieldStruct() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
fields = $customfields-&amp;gt;fetchFieldStruct($id=null, $nohide=false, $table=null, $cachepath=null); // where $id can be a column_name, an ordinal_position from information_schema.columns or null if you want to fetch all the columns at once&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method will either return null, either a single field object, either an array on field objects.&lt;br /&gt;
&lt;br /&gt;
Note: this function will use caching by default to accelerate future results, but you can disable it, or even change the default cache path. For more informations please read the comments in the source code.&lt;br /&gt;
&lt;br /&gt;
=== Fetch several custom fields&#039; SQL structures ===&lt;br /&gt;
&lt;br /&gt;
Use the fetchAllFieldsStruct() method, which is an alias for fetchFieldStruct():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;fetchAllFieldsStruct($nohide=false, $table=null, $cachepath=null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: this method will always return an array of fields objects, even if only one is found (for more consistency), and this is the main difference with fetchFieldStruct(null).&lt;br /&gt;
&lt;br /&gt;
== Managing custom fields records ==&lt;br /&gt;
&lt;br /&gt;
Now that you know how to create and manage the structure of custom fields, we can proceed on to managing the records.&lt;br /&gt;
&lt;br /&gt;
=== Creating a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Simply use the create() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;create($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
$object must be an object containing as its properties the customfields data.&lt;br /&gt;
&lt;br /&gt;
For example, if you have a field named mycustomfield, your object must have $object-&amp;gt;mycustomfields defined.&lt;br /&gt;
&lt;br /&gt;
This method will store in the database ALL the custom fields that were created/edited and with a valid value (eg: an empty value for a field that does not accept null value is invalid) AT ONCE! So you can modify all the columns of one record at once, but not several records at once.&lt;br /&gt;
&lt;br /&gt;
Concretely, this is very useful with Dolibarr, because modules tends to store everything inside a single object generally called $object, and CustomFields tries to hook onto $object, so that you can just pass $object as is to create() and it will save all the custom fields.&lt;br /&gt;
&lt;br /&gt;
But you can also create an empty object with $object = new stdClass(); and then manually fill the custom fields records with whatever you want, and then call the create() method, this will work as well!&lt;br /&gt;
&lt;br /&gt;
You can also just modify only one column of one record, by just specifying one custom field column_name in your $object. The other custom fields that are not specified won&#039;t be created/modified.&lt;br /&gt;
&lt;br /&gt;
=== Updating a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Use the update() method, which is an alias for the create() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;update($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As noted above, update() is an alias of the create() method, which means that CustomFields will try to detect by itself if a record needs to be updated or created.&lt;br /&gt;
&lt;br /&gt;
=== Deleting a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Use the delete() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;delete($id); // where $id is = to fk_moduleid column, NOT rowid&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fetching a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Just use the fetch() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$records = $customfields-&amp;gt;fetch($id=null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where $id can either be = fk_moduleid (NOT rowid), or null to fetch all the records at once.&lt;br /&gt;
&lt;br /&gt;
This method will either return null, a single record object, or an array of records objects.&lt;br /&gt;
&lt;br /&gt;
=== Fetching all custom fields&#039; records ===&lt;br /&gt;
&lt;br /&gt;
Use the fetchAll() method, which is an alias for fetch():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$records = $customfields-&amp;gt;fetchAll();&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: this method will always return an array of records object, for more consistency (and this is the main difference with fetch(null) ).&lt;br /&gt;
&lt;br /&gt;
=== Fetching referenced records of a constrained field ===&lt;br /&gt;
&lt;br /&gt;
A constrained custom field points to another database table, and a value for this constrained field is the rowid of a record in the referenced table.&lt;br /&gt;
&lt;br /&gt;
Thus, the principal purpose of a constrained custom field is not the value of the field in itself (it&#039;s simply an integer), but rather the other fields available from the referenced table, the constrained field being in fact just a pointer.&lt;br /&gt;
&lt;br /&gt;
To fetch the remote record from the referenced table, use the fetchReferencedValuesList() of the CustomFields class:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$fkrecord = fetchReferencedValuesList($field, $id=null, $where=null, $allcolumns=false);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
&lt;br /&gt;
* $field is a custom field object (fetched using fetchFieldStruct() or fetchAllFieldsStruct()),&lt;br /&gt;
* $id is the rowid of the record to get, just like fetch() and fetchAll(). Can be null to fetch all remote records (or to use the $where clause).&lt;br /&gt;
* $where is a string without the WHERE clause (eg: &amp;quot;fk_facture=1&amp;quot;) to get finer control. In this case, you can use $id or set $id=null depending on your purpose. This is mainly a private argument to allow for cascade option.&lt;br /&gt;
* $allcolumns if false, it will fetch only the required columns to do the smart value substitution. If true, it will fetch all remote columns (useful for ODT substitution). If an array of strings, each string will be a column to fetch from the remote database.&lt;br /&gt;
&lt;br /&gt;
Note that this function is NOT recursive (it will fetch the records from the referenced table, and that&#039;s all, it won&#039;t fetch the custom fields of the referenced table nor recursively fetch the remote constrained custom fields). To do recursive fetching, see in the Facade API.&lt;br /&gt;
&lt;br /&gt;
Note2: this function automatically manages Smart Value Substitution depending of the $field-&amp;gt;column_name.&lt;br /&gt;
&lt;br /&gt;
=== Recursive fetching referenced records of a constrained field ===&lt;br /&gt;
&lt;br /&gt;
If you want to recursively fetch a constrained field (eg: constrainedfield1-&amp;gt;ref_table-&amp;gt;constrainedfield2-&amp;gt;ref_table2-&amp;gt;...), you need another function called fetchReferencedValuesRec(). Technically, this function is in the Facade API (inside /customfields/lib/customfields_aux.lib.php), but it isn&#039;t meant to be used by lambda users (contrary to customfields_fill_object()), but is mainly a helper function for the rest of the facade API.&lt;br /&gt;
&lt;br /&gt;
fetchReferencedValuesRec() works very similarly to fetchReferencedValuesList(), but it will work recursively.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$fkrecord = fetchReferencedValuesRec($customfields, $field, $id, $recursive=true)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where $customfields is the CustomFields instance for the module you are working on, $field is the constrained field you want to fetch, $id is the record&#039;s id you want to fetch, and $recursive enables or disables the recursion.&lt;br /&gt;
&lt;br /&gt;
Note that this function automatically manages Smart Value Substitution (and also recursively).&lt;br /&gt;
&lt;br /&gt;
Note2: This function tries to automatically avoid an infinite recursion loop by using a $blacklist: for a given constrained custom field, a table won&#039;t be visited twice. This means that for example you can do: invoice-&amp;gt;user-&amp;gt;socpeople-&amp;gt;invoice, this will work and every referenced fields will be fetched, but it will automatically stop the recursion at the second invoice, because else it could continue in a loop to user, socpeople, invoice, user, socpeople, etc.&lt;br /&gt;
&lt;br /&gt;
Note3: when adding recursively referenced fields, the key in the returned array will be prefixed by the name of the custom field that led to those remote fields. Thus, you can have something like &amp;quot;cf_myfield1_cf_remotefield1_cf_remotefield2&amp;quot; if you have a constrained field cf_myfield1-&amp;gt;cf_remotefield1-&amp;gt;cf_remotefield2.&lt;br /&gt;
&lt;br /&gt;
== Input forms for custom fields ==&lt;br /&gt;
&lt;br /&gt;
The API provides some neat ways to automatically manage the input forms for users to input new values, with the best adapted HTML form based on the custom field&#039;s data type.&lt;br /&gt;
&lt;br /&gt;
The following methods will print input fields that are best adapted to the SQL data type of your custom fields as well as figure out the possible values and even format them (for example: a not null enum will be printed as a HTML select list without an empty option; fetch automatically remote columns for constrained fields and show them in a select).&lt;br /&gt;
&lt;br /&gt;
So you don&#039;t have to bother how your data will be presented to the user nor how it will be processed.&lt;br /&gt;
&lt;br /&gt;
=== Printing an input form adapted to the field&#039;s data type ===&lt;br /&gt;
&lt;br /&gt;
showInputField() is a sort of converter customfieldstructure -&amp;gt; HTML input field.&lt;br /&gt;
&lt;br /&gt;
Use the &#039;&#039;&#039;showInputField()&#039;&#039;&#039; method to print the right input form for a custom field.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;showInputField($field, $currentvalue=null, $moreparam=&#039;&#039;, $ajax_php_callback=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
showInputField() takes 4 parameters:&lt;br /&gt;
&lt;br /&gt;
* $field is a CustomFields structure object (eg: returned by fetchFieldStruct()) and is necessary to detect the right datatype and the possible values.&lt;br /&gt;
* $currentvalue is the current value if you want to preselect a value (for example if the user already defined a value, you can reload it here). This is optional, in case it is not defined, either a null/empty value will be chosen, or either the default value defined when creating the custom field.&lt;br /&gt;
* $moreparam is used to add more HTML attributes, if you want for example to add some css styling or whatever you want.&lt;br /&gt;
* $ajax_php_callback is optional and allows to specify the relative url to the php callback script. If specified, an AJAX script will be automatically attached to this field (see AJAX-PHP callback below).&lt;br /&gt;
&lt;br /&gt;
showInputField() only prints ONE HTML input field, thus you have to enclose it/them by your own HTML form.&lt;br /&gt;
&lt;br /&gt;
eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print &#039;&amp;lt;form method=&amp;quot;get&amp;quot; action=&amp;quot;?&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
print &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;customtoken&amp;quot; value=&amp;quot;ABCDEFGH&amp;quot; /&amp;gt;&#039;; // example of a custom hidden HTML input field, that can be used to pass a token for example&lt;br /&gt;
// Loop over each custom fields (you have to load them before with $customfields-&amp;gt;fetchAllFieldsStruct()&lt;br /&gt;
foreach ($customfields-&amp;gt;fields as $field) {&lt;br /&gt;
    print $customfields-&amp;gt;showInputField($field, $currentvalue=null, $moreparam=&#039;&#039;);&lt;br /&gt;
}&lt;br /&gt;
print &#039;&amp;lt;/form&amp;gt;&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can also store the resulting HTML input field and process it by a regexp for example:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$myhtmlfield = $customfields-&amp;gt;showInputField($field, $currentvalue=null, $moreparam=&#039;&#039;);&lt;br /&gt;
preg_replace($myhtmlfield, ..., ...);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: for constrained fields, if the name follows the convention of &#039;&#039;&#039;Smart Value Substitution&#039;&#039;&#039; (ie: remote columns names are included in the custom field&#039;s name), showInputField() will &#039;&#039;&#039;automatically fetch the remote values, format them, and present them&#039;&#039;&#039; in a HTML select.&lt;br /&gt;
&lt;br /&gt;
=== Printing an input form for a custom field ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;showInputForm()&#039;&#039;&#039; is a function used to print a single (and only one!) custom field with an adapted HTML input field, but enclosed by a HTML form. This is used for example when you want to allow to edit a single custom field but not the others.&lt;br /&gt;
&lt;br /&gt;
showInputForm() is in fact a wrapper for showInputField(), thus it will call showInputField() and simply enclose it by a form.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;showInputForm($id, $field, $currentvalue=null, $idvar=&#039;id&#039;, $page=null, $moreparam=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== AJAX-PHP callback ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;showInputFieldAjax()&#039;&#039;&#039; will output an AJAX script (using jQuery) to send and receive data to and from a specified php script. The AJAX script will automatically manage HTML form input&#039;s updating as long as the received data follows some convention.&lt;br /&gt;
&lt;br /&gt;
An AJAX script can be attached to any field (including non-CustomFields) by using the method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;showInputFieldAjax($id, $php_callback_url, $on_func=&amp;quot;change&amp;quot;, $request_type=&amp;quot;post&amp;quot;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
* $id is the HTML id of the field you want to attach the AJAX to.&lt;br /&gt;
* $phpcallback the relative path from Dolibarr&#039;s htdocs root folder to the php script that will receive and send back data.&lt;br /&gt;
* $on_func is the Javascript or jQuery event that should trigger the AJAX script (by default on change).&lt;br /&gt;
* $request_type is the HTML query type that will be used to send the data to the PHP callback (either &amp;quot;get&amp;quot; or &amp;quot;post&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
The AJAX script will automatically manage sending/receiving of data:&lt;br /&gt;
&lt;br /&gt;
* automatically send all form&#039;s inputs values, as well as the current calling field&#039;s name and value. The sending happens on change by default, but another event can be set using the $on_event argument. Data will be sent using standard GET or POST.&lt;br /&gt;
* Upon reception of data from the php script, the AJAX script will automatically parse the data and update the HTML fields.&lt;br /&gt;
&lt;br /&gt;
The PHP callback script must send back to AJAX the data in JSON encoded format, and with with a specific format:&lt;br /&gt;
&lt;br /&gt;
* the data must be an associative array, where each entry&#039;s key is the name (not ID!) of an HTML field to update.&lt;br /&gt;
* each entry&#039;s value is an associative array, where each sub-entry&#039;s key is the action to do. Available actions: &#039;options&#039;, &#039;value&#039;, &#039;html&#039;, &#039;alert&#039;.&lt;br /&gt;
* each sub-entry&#039;s value is the value for this action and HTML field.&lt;br /&gt;
&lt;br /&gt;
Thus, this approach allows to generically support a wide range of updating actions, managed automatically.&lt;br /&gt;
&lt;br /&gt;
You, the implementer, have just to edit the PHP callback script and return an associative array defining what action you want for each field you want to update. This is exactly what has been implemented in the Custom AJAX Functions library, which simplifies even more your job by managing automatically the step described here (generating AJAX and formatting data), so that you can focus on &#039;&#039;&#039;making the data&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Printing custom fields in documents ==&lt;br /&gt;
&lt;br /&gt;
The API can also automatically manage the printing and formatting of custom fields in your ODT, PDF and HTML documents.&lt;br /&gt;
&lt;br /&gt;
=== Printing in HTML ===&lt;br /&gt;
&lt;br /&gt;
You can either just print the value as-is, or pass them through the printField() or simpleprintField() function to format them (very useful for constrained fields) - see below.&lt;br /&gt;
&lt;br /&gt;
=== Printing in documents ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;printField()&#039;&#039;&#039; is used to properly print and translate a custom field&#039;s record&#039;s value. Generally, you can just print the custom field&#039;s value as-is, but in a few cases (eg for boolean types, or constrained fields), using the printField() method will more properly print the value.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;printField($field, $value, $outputlangs=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
* $field is the custom field&#039;s SQL structure as an object (eg: returned by fetchFieldStruct()).&lt;br /&gt;
* $value is the custom field&#039;s value for the current record (eg: $customfields-&amp;gt;fields-&amp;gt;myfield).&lt;br /&gt;
* $outputlangs is the Translate object for the target language.&lt;br /&gt;
&lt;br /&gt;
For example, with a constrained field with Smart Value Substitution, instead of printing the id like 1, it will print the full name of user like FirstName Name.&lt;br /&gt;
&lt;br /&gt;
This method will also translate a value if a translation is found in the language files.&lt;br /&gt;
&lt;br /&gt;
Another method &#039;&#039;&#039;printFieldPDF()&#039;&#039;&#039; can be used to properly print a value for a PDF (properly encoding the characters).&lt;br /&gt;
&lt;br /&gt;
Powertip: this function can also be used to get the Smart Value Substituted value of the field (instead of printing the rowid) in the field is constrained.&lt;br /&gt;
&lt;br /&gt;
=== Simple printing in documents ===&lt;br /&gt;
&lt;br /&gt;
If you don&#039;t want to have to manually fetch the structure of every custom field whose you want to print the value, you can then use a simplifier method called &#039;&#039;&#039;simpleprintField()&#039;&#039;&#039; which will take as input a custom field name instead of a custom field SQL structure object.&lt;br /&gt;
&lt;br /&gt;
This method is just a simplifier (Facade) wrapper for the printField() method, and thus will have the same advantages (fetch constrained fields, values translation, etc.).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;simpleprintField($fieldname, $value, $outputlangs=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
* $fieldname is the custom field&#039;s name (eg: &amp;quot;myfield&amp;quot;).&lt;br /&gt;
* $value is the custom field&#039;s value for the current record (eg: $customfields-&amp;gt;fields-&amp;gt;myfield).&lt;br /&gt;
* $outputlangs is the Translate object for the target language.&lt;br /&gt;
&lt;br /&gt;
Another method &#039;&#039;&#039;simpleprintFieldPDF()&#039;&#039;&#039; can be used to properly print a value for a PDF (properly encoding the characters).&lt;br /&gt;
&lt;br /&gt;
Powertip: this function can also be used to get the Smart Value Substituted value of the field (instead of printing the rowid) in the field is constrained.&lt;br /&gt;
&lt;br /&gt;
=== Find the label ===&lt;br /&gt;
&lt;br /&gt;
Finding the label of a custom field might not be as simple as it seems. First, values of properties objects may be easily used, but their name (key) is a worst job. Secondly, custom fields&#039; labels can be translated, and the following functions are expressly made for that purpose. Thirdly, for PDF and some other documents you have to encode the characters if you want them to properly be drawn.&lt;br /&gt;
&lt;br /&gt;
So if you want to print a custom field&#039;s label with translation (multilingual) support, just use the &#039;&#039;&#039;findLabel()&#039;&#039;&#039; method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$translatedfieldname = $customfields-&amp;gt;findLabel($fieldname, $outputlangs = &#039;&#039;); // where $fieldname is just a custom field label (eg: cf_mycustomfield or mycustomfield, both will work) and $outputlangs is the target language object (see the Translate class native of Dolibarr)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As for the other printing methods, you also have the &#039;&#039;&#039;findLabelPDF()&#039;&#039;&#039; wrapper method to easily print labels that with characters encoded for PDF.&lt;br /&gt;
&lt;br /&gt;
= Facade API =&lt;br /&gt;
&lt;br /&gt;
The Facade API was made to ease the usage of the CustomFields class with the Dolibarr system. It provides a neat way to do many operations with single commands, and will automatically manage the loading of the CustomFields class, and its usage with many optimizations.&lt;br /&gt;
&lt;br /&gt;
== customfields_fill_object() ==&lt;br /&gt;
&lt;br /&gt;
You can load the Facade API and then use the &#039;&#039;&#039;customfields_fill_object()&#039;&#039;&#039; to automatically fetch the fields structures and the records, and also automatically link to remote fields when a constrained field is found. This is an all-in-one solution to easily fetch CustomFields datas, and is also optimized to fully use caching and other special properties of the CustomFields class.&lt;br /&gt;
&lt;br /&gt;
Concretely, you just have to include the Facade API, then pass the customfields_fill_object() with a Dolibarr $object, and the Facade API will return a CustomFields object with appended the custom fields records and also the custom fields&#039; SQL structures.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$customfields = customfields_fill_object($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* $object will then include the custom fields records associated with this object (eg: $object-&amp;gt;customfields-&amp;gt;cf_yourfield).&lt;br /&gt;
* $customfields will be a CustomFields object containing the fields structures (eg: $customfields-&amp;gt;fields-&amp;gt;yourfield).&lt;br /&gt;
&lt;br /&gt;
You can also call any CustomFields function if you need to using the $customfields object, eg: $customfields-&amp;gt;simpleprintField($object-&amp;gt;customfields-&amp;gt;cf_yourfield);&lt;br /&gt;
&lt;br /&gt;
Note: $object can also be a dummy object containing only two properties that are required: $object-&amp;gt;id and $object-&amp;gt;table_element.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will now describe the full specification of this function:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$customfields = customfields_fill_object(&amp;amp;$object,$fromobject = null, $outputlangs = null, $prefix = null,$pdfformat = false);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* $object is the object where the customfields will be saved&lt;br /&gt;
* $fromobject is the object from where the customfields will be fetched (this can be a different module than $object, or can be null to be the same as $object)&lt;br /&gt;
* $outputlangs is the target language for translation of fields value (if empty, the default current global translation object will be used)&lt;br /&gt;
* $prefix allows you to save the custom fields in a sub-object. By default, when $prefix=null, custom fields are saved in the path $object-&amp;gt;customfields, but when you specify a prefix, custom fields will be saved inside $object-&amp;gt;customfields-&amp;gt;$prefix. This may be useful if you want to store multiple object&#039;s custom fields or records in the same $object.&lt;br /&gt;
* $pdfformat defines if you want a special formatting for the values: if null, there&#039;s no formatting, if false, there is a simple formatting (eg: translation of fields&#039; values), if true then the formatting will be adapted for PDF templating.&lt;br /&gt;
&lt;br /&gt;
For more informations on pratical usages of this function, please [[Module_CustomFields#Implementing_in_php_code_.28dolibarr_core_modules_or_your_own_module.29|read the user documentation on how to implement CustomFields in php code]] or [[Module_CustomFields_Cases#Linking_Dolibarr_objects_from_two_different_modules|read an example case of linking two objects of different modules together]].&lt;br /&gt;
&lt;br /&gt;
== customfields_fill_object_lines() ==&lt;br /&gt;
&lt;br /&gt;
Similar to customfields_fill_object() for normal custom fields, there is also a &#039;&#039;&#039;customfields_fill_object_lines()&#039;&#039;&#039; to fetch custom fields associated with lines of products/services. This function is also optimized and it is strongly advised to use it and to not try to fetch manually this kind of custom fields (ie: associated with lines of products/services rather than objects), because there are a lot of optimizations going on behind the scene and that are very hard to do manually.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields for lines of products/services (you can then access them by doing $object-&amp;gt;customfields-&amp;gt;lines-&amp;gt;$lineid-&amp;gt;cf_yourfield)&lt;br /&gt;
$customfields = customfields_fill_object_lines($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Requirements: $object can be a dummy object but must contain at least 2 properties: $object-&amp;gt;id and $object-&amp;gt;table_element_line.&lt;br /&gt;
&lt;br /&gt;
For more informations on pratical usages of this function, please [[Module_CustomFields#Implementing_in_php_code_.28dolibarr_core_modules_or_your_own_module.29|read the user documentation on how to implement CustomFields in php code]].&lt;br /&gt;
&lt;br /&gt;
= Architecture =&lt;br /&gt;
Here is a full list of the CustomFields packaged files with a short description (for a more in-depth view just crawl the source files, they are full of comments):&lt;br /&gt;
&lt;br /&gt;
== Core files ==&lt;br /&gt;
files that are necessary for the CustomFields to work, they contains the core functions&lt;br /&gt;
&lt;br /&gt;
* /customfields/admin/customfields.php --- Administrator&#039;s configuration panel : this is where you create and manage the custom fields definitions&lt;br /&gt;
* /customfields/class/actions_customfields.class.php --- Hooks class : used to hook into Dolibarr core modules without altering any core file (can be used to hook into your own modules too). Also used to preprocess the parameters before calling the printforms library.&lt;br /&gt;
* /customfields/class/customfields.class.php --- Core class : every database action is made here in this class. You can find some printing functions because they are very generic.&lt;br /&gt;
* /customfields/conf/conf_customfields.lib.php --- Configuration file : contains the main configurable variables to adapt CustomFields to your needs or to expand its support to other modules and more native sql types.&lt;br /&gt;
* /customfields/conf/conf_customfields_func.lib.php --- Auxiliary functions library to read and manage config variables&lt;br /&gt;
* /customfields/core/modules/modCustomFields.class.php --- Dolibarr&#039;s module definition file : this is a core file necessary for Dolibarr to recognize the module and to declare the hooks to Dolibarr (but it does not store anything else than meta-informations).&lt;br /&gt;
* /customfields/core/substitutions/functions_customfields.lib.php --- CustomFields substitution class for ODT generation : necessary to support customfields tags in ODT files&lt;br /&gt;
* /customfields/core/triggers/interface_50_modCustomFields_SaveFields.class.php --- Core triggers file : this is where the actions on records are managed. This is an interface between other modules and CustomFields management. You should not modify this file but you should add your triggers in $triggersarray inside conf_customfields.lib.php, but if you really have a special need and know what you do, you can edit this file to directly add your own triggers. Also, if CustomFields are shown in the forms but cannot be modified nor saved, then probably this trigger file is not detected by Dolibarr (maybe you can try to lower the number 50 to raise the priority?).&lt;br /&gt;
* /customfields/fields/customfields_fields_extend.lib.php.example --- Overloading functions library with users functions: this is where implementers can use their own php code to manage the custom fields they want (one function per action and per field).&lt;br /&gt;
* /customfields/fields/customfields_fields_ajax_custom.lib.php.example --- Custom AJAX functions library with users functions: this is where implementers can use their own php code to manage the custom fields cascading and AJAX calls, similarly to the Overloading functions library (one function per action and per field).&lt;br /&gt;
* /customfields/langs/code_CODE/customfields.lang --- Core language file : this is where you can translate the admin config panel (data types names, labels, descriptions, etc.)&lt;br /&gt;
* /customfields/langs/code_CODE/customfields-user.lang --- User defined language file : this is where you can store the labels and values of your custom fields (see the related chapter)&lt;br /&gt;
* /customfields/lib/customfields_aux.lib.php --- Simplifier library to easily use CustomFields class to populate an $object with custom fields datas (Facade design pattern). This is what you should use for most of your needs.&lt;br /&gt;
* /customfields/lib/customfields_printforms.lib.php --- Core printing library for records : contains only printing functions, there&#039;s not really any core functions inside but it allows to manage the printing of the custom fields records and their editing.&lt;br /&gt;
* /customfields/lib/customfields_ajax_wrapper.lib.php --- AJAX wrapper library to automatically manage cascading at objects creation. This isn&#039;t really necessary for CustomFields to function, this is more an addition to allow dynamical AJAX updating, but CustomFields core functionalities can work without this library.&lt;br /&gt;
* /customfields/sql/*.sql --- Unused (the tables are created directly via a function in the customfields.class.php) - but the sql files are still valid, they were used as squeletton in the first designs.&lt;br /&gt;
&lt;br /&gt;
== PDFTest module ==&lt;br /&gt;
optional module to append a page listing all custom fields values on newly generated PDF documents. To be used to test if custom fields are working correctly before making one&#039;s own template.&lt;br /&gt;
&lt;br /&gt;
* /customfieldspdftest/class/actions_customfieldspdftest.class.php --- Main functions library and hooks class, to hook onto pdf generation. Contains the PDFTest functions.&lt;br /&gt;
* /customfieldspdftest/core/modules/modCustomFieldsPDFTest.class.php --- Module descriptor for Dolibarr&lt;br /&gt;
&lt;br /&gt;
== Runtime outline ==&lt;br /&gt;
&lt;br /&gt;
Here is an outline of how CustomFields works: what functions will be called and why:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At admin page:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* No external function call (apart for some icons or needed admin functions like for tabs), everything that is done are sql queries via CustomFields class (to fetch and modify the sql columns in the customfields tables).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At printing:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Module&#039;s hook formObjectOptions calls Dolibarr&#039;s hookmanager&lt;br /&gt;
* Hookmanager calls CustomFields /class/actions_customfields.class.php, function formObjectOptions() of CustomFields&lt;br /&gt;
* Preprocess a few parameters (mainly the $action) and calls customfields_print_forms()&lt;br /&gt;
* customfields_print_forms() preprocess all the hook&#039;s parameters and assimilate them with the CustomFields&#039;s configuration parameters (there&#039;s no connection to the database at this stage, only parameters preprocessing), then calls /lib/customfields_printforms.lib.php, functions customfields_print_creation_form() or customfields_print_datasheet_form&lt;br /&gt;
* both customfields_print_creation_form() lookup the database using CustomFields class and prints empty custom fields; customfields_print_datasheet_form() lookup the database using CustomFields class, fetch the values of the custom fields and prints them;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At edition:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Same steps as &#039;&#039;&#039;At printing&#039;&#039;&#039;&lt;br /&gt;
* then customfields_print_datasheet_form() process the custom field being edited differently than the others: if editing, it prints the html input field (to enter a new value), or if saving it just saves the data into the database by calling the CUSTOMFIELDS_MODIFY trigger and then continue to print the other fields normally.&lt;br /&gt;
* CUSTOMFIELDS_MODIFY trigger calls $customfields-&amp;gt;update($newrecord);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At creation (saving):&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Module&#039;s creation trigger MYMODULE_CREATE calls Dolibarr&#039;s run_trigger()&lt;br /&gt;
* Dolibarr run_trigger() calls CustomFields trigger file /core/trigger/interface_50_modCustomFields_SaveFields.class.php, function run_trigger()&lt;br /&gt;
* CustomFields&#039;s run_trigger() assimilate the given parameters with the CustomFields&#039;s config parameters, and if the trigger is found in $triggersarray, then the appropriate action is taken (by default: _CREATE or another standard action if recognized), if not in $triggersarray, the function will still try to automatically detect the appropriate action by detecting MYMODULE_ALISTOFACTIONS with a regular expression.&lt;br /&gt;
* The appropriate action is then done (creation in this case) by calling $customfields-&amp;gt;create($record).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At edition or creation of product lines:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Similar to &#039;&#039;&#039;At edition&#039;&#039;&#039; but with hooks: formCreateProductOptions and formEditProductOptions.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At ODT document generation and substitution:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Module&#039;s ODT generator calls for Dolibarr&#039;s function complete_substitution_array(), which then calls for CustomFields substitution file /core/substitution/functions_customfields.lib.php, function customfields_completesubstitutionarray()&lt;br /&gt;
* CustomFields&#039;s function customfields_completesubstitutionarray() calls the simplifier library customfields_aux.lib.php, function customfields_fill_object() and make the substitution inside the $substitutionarray.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At ODT document generation and substitution for product lines:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Same as the previous point but this function is called for every product lines, and thus the data is cached. Also the simplifier function is here called customfields_fill_object_lines(), and the CustomFields&#039;s substitution function called customfields_completesubstitutionarray_lines().&lt;br /&gt;
&lt;br /&gt;
= How to add the support of a new module =&lt;br /&gt;
&lt;br /&gt;
There are two cases: either the module already implements hooks, and you just have to configure CustomFields to support it, or either the module does not implements hooks nor triggers.&lt;br /&gt;
&lt;br /&gt;
This chapter will guide you through the steps to make any module supported by CustomFields (be it a Dolibarr core module or your own module).&lt;br /&gt;
&lt;br /&gt;
== Preliminary test ==&lt;br /&gt;
&lt;br /&gt;
This preliminary test will allow you to know at a glance how much work needs to be done in order to make your module supported by CustomFields, and it will also give you all the config parameters if they are available.&lt;br /&gt;
&lt;br /&gt;
Important note: this test will only give data about hooks, which is the main part to make CustomFields work for your module. But for triggers, there is no way to test, so you will have to check the triggers manually (either by testing if the custom fields are saved at creation page, or by looking at the source code of the objects classes).&lt;br /&gt;
&lt;br /&gt;
Edit the php script of the module (check the URL when you access the module&#039;s datasheet or creation page - attention both are two different pages, so you have to do this twice), then put this at the very &#039;&#039;&#039;end&#039;&#039;&#039; of the php script:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// CustomFields debuglines to show config parameters for current module&lt;br /&gt;
print(&amp;quot;&amp;lt;pre&amp;gt;&amp;quot;); // html nice output&lt;br /&gt;
print(&amp;quot;table_element: {$object-&amp;gt;table_element} - table_element_line: {$object-&amp;gt;table_element_line} - contexts: &amp;quot;);&lt;br /&gt;
print_r($hookmanager-&amp;gt;contextarray);&lt;br /&gt;
print(&amp;quot; - conf hooks_modules: &amp;quot;);&lt;br /&gt;
print_r($conf-&amp;gt;hooks_modules);&lt;br /&gt;
print(&amp;quot; - hooks attached: &amp;quot;);&lt;br /&gt;
print_r($hookmanager-&amp;gt;hooks);&lt;br /&gt;
print(&amp;quot; - user rights: &amp;quot;);&lt;br /&gt;
print_r($user-&amp;gt;rights);&lt;br /&gt;
print(&amp;quot;&amp;lt;/pre&amp;gt;&amp;quot;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now reload the page, and scroll at the very bottom. You should see some informations about the module.&lt;br /&gt;
&lt;br /&gt;
Depending on the data available, you will know what you have to do:&lt;br /&gt;
&lt;br /&gt;
* If all the datas is available, then it&#039;s ok, jump to [[Module_CustomFields_Dev#Configuring_CustomFields_to_support_the_module]] and copy these datas to the parameters of conf_customfields.lib.php&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;table_element&#039;&#039;&#039;: $object is not defined, try to find how is called the main object variable and replace $object by this variable in the debug code given above (eg: in Agenda, the object variable is called $agenda and $agendacomm).&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;table_element_line&#039;&#039;&#039;: either there&#039;s no products lines for this module, either the main object variable is not called $object (see the previous point). One last case is that the object is not standard and does not store table_element_line (the name of the table that stores its products lines), in which case you have to manually add it in the class (eg: $this-&amp;gt;table_element_line = &#039;something&#039;).&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;contexts&#039;&#039;&#039;: the hookmanager is not defined, and probably there&#039;s no hook at all in this module, so you&#039;ll have to implement them by yourself. Jump to [[Module_CustomFields_Dev#Hooks]].&lt;br /&gt;
&lt;br /&gt;
* If there is a list of &#039;&#039;&#039;contexts&#039;&#039;&#039; but the module cannot be found in the list: the hookmanager is defined but the module has not defined a context, you still have to implement the hooks. Jump to [[Module_CustomFields_Dev#Hooks]].&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;conf hooks_modules&#039;&#039;&#039;: this variable is not necessary, empty or not it doesn&#039;t matter. But if you see customfields and/or customfieldspdftest inside the list, it is a confirmation that CustomFields indeed is hooking the module correctly.&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;hooks attached&#039;&#039;&#039;, or CustomFields cannot be found in the list: CustomFields has not been properly configured to hook to this module, or you forgot to disable then re enable CustomFields in order to update the CustomFields&#039;s hooks. Jump to [[Module_CustomFields_Dev#Configuring_CustomFields_to_support_the_module]].&lt;br /&gt;
&lt;br /&gt;
* If there is no &#039;&#039;&#039;$user-&amp;gt;rights&#039;&#039;&#039;: there is either no standard security implemented in this module, or either the variable is called with another name. If there is no security, you don&#039;t need to configure it in CustomFields, but if there is a security, you need to find how the variable is called (like $object can get another name sometimes).&lt;br /&gt;
&lt;br /&gt;
* If there is &#039;&#039;&#039;$user-&amp;gt;rights&#039;&#039;&#039; but the module can&#039;t be found in the list: either the name for the rights is different (this often happens, the rights array is not yet standardized), or either the rights are hardcoded into the module&#039;s code (rights should be announced to the $user-&amp;gt;rights array, but sometimes it happens that the dev forgot to do so, and thus the right is entirely hardcoded and you can&#039;t find it in the dynamic rights array). In any of these two cases, you will have to manually read the module&#039;s code to find the right. Also, maybe there is no security implemented, but since $user-&amp;gt;rights is declared this is less probable.&lt;br /&gt;
&lt;br /&gt;
Here is an example of output on a correctly hooked module (invoice):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
table_element: facture - table_element_line: facturedet - contexts: Array&lt;br /&gt;
(&lt;br /&gt;
    [0] =&amp;gt; searchform&lt;br /&gt;
    [1] =&amp;gt; leftblock&lt;br /&gt;
    [2] =&amp;gt; toprightmenu&lt;br /&gt;
    [3] =&amp;gt; invoicecard&lt;br /&gt;
)&lt;br /&gt;
 - conf hooks_modules: Array&lt;br /&gt;
(&lt;br /&gt;
    [customfields] =&amp;gt; invoicecard:propalcard:productcard:ordercard:thirdpartycard:contactcard:ordersuppliercard:invoicesuppliercard:membercard:actioncard:projectcard:projecttaskcard:contractcard:interventioncard:doncard:tripsandexpensescard:taxvatcard:expeditioncard:invoicecard:propalcard:ordercard:ordersuppliercard:invoicesuppliercard&lt;br /&gt;
    [customfieldspdftest] =&amp;gt; Array&lt;br /&gt;
        (&lt;br /&gt;
            [0] =&amp;gt; pdfgeneration&lt;br /&gt;
            [1] =&amp;gt; ordersuppliercard&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
)&lt;br /&gt;
 - hooks attached: Array&lt;br /&gt;
(&lt;br /&gt;
    [invoicecard] =&amp;gt; Array&lt;br /&gt;
        (&lt;br /&gt;
            [customfields] =&amp;gt; ActionsCustomFields Object&lt;br /&gt;
                (&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
)&lt;br /&gt;
 - user rights: stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [user] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [user] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [lire] =&amp;gt; 1&lt;br /&gt;
                    [creer] =&amp;gt; 1&lt;br /&gt;
                    [password] =&amp;gt; 1&lt;br /&gt;
                    [supprimer] =&amp;gt; 1&lt;br /&gt;
                    [export] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [self] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [creer] =&amp;gt; 1&lt;br /&gt;
                    [password] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [user_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [readperms] =&amp;gt; 1&lt;br /&gt;
                    [write] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [self_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [readperms] =&amp;gt; 1&lt;br /&gt;
                    [writeperms] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [group_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [read] =&amp;gt; 1&lt;br /&gt;
                    [readperms] =&amp;gt; 1&lt;br /&gt;
                    [write] =&amp;gt; 1&lt;br /&gt;
                    [delete] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    [facture] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [lire] =&amp;gt; 1&lt;br /&gt;
            [creer] =&amp;gt; 1&lt;br /&gt;
            [invoice_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [unvalidate] =&amp;gt; 1&lt;br /&gt;
                    [send] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [valider] =&amp;gt; 1&lt;br /&gt;
            [paiement] =&amp;gt; 1&lt;br /&gt;
            [supprimer] =&amp;gt; 1&lt;br /&gt;
            [facture] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [export] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    [propale] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [lire] =&amp;gt; 1&lt;br /&gt;
            [creer] =&amp;gt; 1&lt;br /&gt;
            [valider] =&amp;gt; 1&lt;br /&gt;
            [propal_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [send] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [cloturer] =&amp;gt; 1&lt;br /&gt;
            [supprimer] =&amp;gt; 1&lt;br /&gt;
            [export] =&amp;gt; 1&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    [produit] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [lire] =&amp;gt; 1&lt;br /&gt;
            [creer] =&amp;gt; 1&lt;br /&gt;
            [supprimer] =&amp;gt; 1&lt;br /&gt;
            [export] =&amp;gt; 1&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    etc... a lot of other rights...&lt;br /&gt;
&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When you are finished with implementing the support of CustomFields in the module, you should delete these debug lines.&lt;br /&gt;
&lt;br /&gt;
== Implementing the support of CustomFields in the module ==&lt;br /&gt;
&lt;br /&gt;
If there is a module you want to implement the support of CustomFields, but does not yet has the required components, here is a guide on how to implement these components.&lt;br /&gt;
&lt;br /&gt;
=== Hooks ===&lt;br /&gt;
&lt;br /&gt;
Implementing hooks will allow custom fields to appear on your module&#039;s page. They will also be editable and they will save the data. For creation, you need to use a trigger (see the subchapter below).&lt;br /&gt;
&lt;br /&gt;
First you should be familiar with the way that hooks are implemented in modules. If you don&#039;t, please first [[Hooks_system#Implementation|read the hooks documentation]].&lt;br /&gt;
&lt;br /&gt;
An example case of implementation of hooks into a Dolibarr core file can be found in the [[Module_CustomFields_Cases|CustomFields Example Cases wiki page]].&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Datasheet hook&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
To implement CustomFields in your module, you must initialize the hook manager, then use the following hook context:&lt;br /&gt;
* formObjectOptions&lt;br /&gt;
&lt;br /&gt;
This hook must be placed everywhere in your code where you want custom fields to appear (eg: on the creation page and datasheet view page).&lt;br /&gt;
&lt;br /&gt;
Note: Generally, you will have to place the hook just before the end of a table tag (&#039;&#039;&#039;&amp;lt;/table&amp;gt;&#039;&#039;&#039;), so if you don&#039;t know where to place it (eg: if you&#039;re implementing CustomFields on a Dolibarr core module), just search for this tag in the page shown in the url (eg: /htdocs/commande/fiche.php).&lt;br /&gt;
&lt;br /&gt;
Eg: First load the hookmanager at the beginning of your php script:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Initialize technical object to manage hooks of thirdparties. Note that conf-&amp;gt;hooks_modules contains array array&lt;br /&gt;
include_once(DOL_DOCUMENT_ROOT.&#039;/core/class/hookmanager.class.php&#039;);&lt;br /&gt;
$hookmanager=new HookManager($db);&lt;br /&gt;
$hookmanager-&amp;gt;initHooks(array(&#039;yourmodule&#039;));&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then place the hook as many times as required:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// ... a lot of php code ...&lt;br /&gt;
&lt;br /&gt;
print &#039;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&#039;; // end of the last row of a table&lt;br /&gt;
&lt;br /&gt;
// Other attributes&lt;br /&gt;
$parameters=array(&#039;colspan&#039; =&amp;gt; &#039; colspan=&amp;quot;3&amp;quot;&#039;); // specifying the colspan is optional but will help CustomFields know how to correctly print the table&lt;br /&gt;
$reshook=$hookmanager-&amp;gt;executeHooks(&#039;formObjectOptions&#039;,$parameters,$object,$action);    // Note that $action and $object may have been modified by hook&lt;br /&gt;
&lt;br /&gt;
print &#039;&amp;lt;/table&amp;gt;&#039;; // end of the table, this is what you&#039;re looking for, place the hook just above&lt;br /&gt;
&lt;br /&gt;
// ... a lot of php code ... redo the same if you find another &amp;lt;/table&amp;gt; and there&#039;s a page where there&#039;s no custom fields but you want to place custom fields on there&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note2: If after implementing the hook you receive an error or custom fields don&#039;t appear, you may have to change $object to another variable, depending on what is called the main object (eg: in Agenda it&#039;s called $action and $actioncomm).&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Lines hooks&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
For product lines, you should anyway use the product lines template files: htdocs/core/tpl/freeproductline_edit.tpl.php&lt;br /&gt;
&lt;br /&gt;
If you don&#039;t, you can still manually implement the required hooks, what is required is that you supply $line (which is the current line object being processed) in the $parameters array:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$parameters=array(&#039;line&#039;=&amp;gt;$line);&lt;br /&gt;
&lt;br /&gt;
echo $hookmanager-&amp;gt;executeHooks(&#039;formEditProductOptions&#039;,$parameters,$object,$action);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The two important things here are that you supply:&lt;br /&gt;
* $line: the line object, with a $line-&amp;gt;rowid property.&lt;br /&gt;
* $object: the parent object of the line, with a $object-&amp;gt;rowid property. For example, if $line is an invoice line, then $object must be the parent invoice object. This is necessary so that CustomFields can automatically recognize the link between the twos.&lt;br /&gt;
&lt;br /&gt;
Possible hooks names:&lt;br /&gt;
* formEditProductOptions: called when you edit a line.&lt;br /&gt;
* formCreateProductOptions: called when you create a line. $line is not required here, because we create a new product line! you can leave $parameters=array(); empty.&lt;br /&gt;
Note: there&#039;s no hook for viewing, because showing the customfields will clutter the visual field, but if you really want you can also do it by adding a formViewProductOptions hook.&lt;br /&gt;
&lt;br /&gt;
=== Triggers ===&lt;br /&gt;
&lt;br /&gt;
First you need to know how to implement triggers in Dolibarr. For more informations about how to implement triggers in your module, please read the [[Triggers#Manage_and_create_a_new_trigger.27s_action|triggers wiki page]].&lt;br /&gt;
&lt;br /&gt;
An example case of implementation of hooks into a Dolibarr core file can be found in the [[Module_CustomFields_Cases|CustomFields Example Cases wiki page]].&lt;br /&gt;
&lt;br /&gt;
Implementing triggers on your module will allow CustomFields to save the fields at creation (datasheet edition is already managed by hooks).&lt;br /&gt;
&lt;br /&gt;
You need to implement 3 triggers:&lt;br /&gt;
- on creation of object - eg: YOURMODULE_CREATE&lt;br /&gt;
- on creation of lines - eg: YOURMODULELINE_INSERT (optional, only if your module has product lines)&lt;br /&gt;
- on update of lines - eg: YOURMODULELINE_UPDATE (optional, only if your module has product lines)&lt;br /&gt;
&lt;br /&gt;
Note: you need to supply the sql id of the newly created object before calling the trigger.&lt;br /&gt;
&lt;br /&gt;
Eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$this-&amp;gt;id = $this-&amp;gt;db-&amp;gt;last_insert_id(MAIN_DB_PREFIX.&amp;quot;yourmodule_table_element&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
// Appel des triggers&lt;br /&gt;
include_once(DOL_DOCUMENT_ROOT . &amp;quot;/core/class/interfaces.class.php&amp;quot;);&lt;br /&gt;
$interface=new Interfaces($this-&amp;gt;db);&lt;br /&gt;
$result=$interface-&amp;gt;run_triggers(&#039;YOURMODULE_CREATE&#039;,$this,$user,$langs,$conf);&lt;br /&gt;
if ($result &amp;lt; 0) {&lt;br /&gt;
    $error++; $this-&amp;gt;errors=$interface-&amp;gt;errors;&lt;br /&gt;
}&lt;br /&gt;
// Fin appel triggers&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note2: if it&#039;s a Dolibarr core module, you can easily find the right place to put the trigger by searching for &#039;create sql&#039; in the class subfolder (eg: URL is /htdocs/commande/fiche.php, the class is in /htdocs/commande/class/commande.class.php).&lt;br /&gt;
&lt;br /&gt;
Note3: CustomFields also supports a few other triggers like _CLONE, _UPDATE, _MODIFY, but they are not required and may not work depending on how your module works to manage these actions.&lt;br /&gt;
&lt;br /&gt;
== Configuring CustomFields to support the module ==&lt;br /&gt;
&lt;br /&gt;
=== Preliminary work ===&lt;br /&gt;
&lt;br /&gt;
You need to get the parameters of this module, and you will then put them in the CustomFields configuration file (conf_customfields.lib.php).&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Getting the parameters automatically&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
You can use the [[Module_CustomFields_Dev#Preliminary_test|preliminary test]] to easily get all those parameters.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Getting the parameters manually&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
To configure CustomFields to support a module, you need at least 5 parameters:&lt;br /&gt;
* the module&#039;s table_name (name of the sql table where the module stores its datas)&lt;br /&gt;
* the module&#039;s context (name given by the module to the hookmanager)&lt;br /&gt;
* the module&#039;s idvar (name of the variable that holds the sql row id of the record)&lt;br /&gt;
* the module&#039;s trigger action(s) (triggers on creation and update, see the related chapter above)&lt;br /&gt;
* the module&#039;s rights (for security purposes)&lt;br /&gt;
&lt;br /&gt;
To get these parameters manually, you can take a look inside the code of the module&#039;s files you want to implement:&lt;br /&gt;
* module&#039;s table name: either find it by yourself in the database (looks like llx_themodulename) or by printing it in actions_customfields.class.php by adding &#039;&#039;&#039;print_r($parameters)&#039;&#039;&#039; and search for $parameters-&amp;gt;table_element&lt;br /&gt;
* module&#039;s context: search for &amp;quot;callHooks(&amp;quot; without the quotes or take a look at the wiki: http://wiki.dolibarr.org/index.php/Hooks_system&lt;br /&gt;
or you can get it by printing it in the /htdocs/customfields/class/actions_customfields.class.php by adding print_r($parameters) and search for $parameters-&amp;gt;context&lt;br /&gt;
* module&#039;s trigger actions: search for &amp;quot;run_triggers(&amp;quot; or take a look at the wiki: http://wiki.dolibarr.org/index.php/Triggers#List_of_known_triggers_actions&lt;br /&gt;
* module&#039;s idvar: look at the url when you access one of the module&#039;s record, and you should see something like fiche.php?xxxid=12 - you need the xxxid (eg: socid, facid, etc.).&lt;br /&gt;
* module&#039;s security: in the page shown in URL when accessing the module&#039;s datasheet, edit this file and add &#039;&#039;&#039;print($user-&amp;gt;rights)&#039;&#039;&#039;, then in the list try to find the correct right (generally something like $user-&amp;gt;modulename-&amp;gt;creer). You can also try to search for $user-&amp;gt;rights in the sourcecode of this module.&lt;br /&gt;
&lt;br /&gt;
With these values, edit the config file (/htdocs/customfields/conf/conf_customfields.lib.php), particularly the $modulesarray and $triggersarray variables:&lt;br /&gt;
&lt;br /&gt;
=== Configure hooks and main parameters ===&lt;br /&gt;
&lt;br /&gt;
Add every parameters except triggers in the $modulesarray:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$modulesarray = array(&lt;br /&gt;
                      array(&#039;context&#039;=&amp;gt;&#039;invoicecard&#039;, &#039;table_element&#039;=&amp;gt;&#039;facture&#039;, &#039;idvar&#039;=&amp;gt;&#039;facid&#039;, &#039;rights&#039;=&amp;gt;array(&#039;$user-&amp;gt;rights-&amp;gt;facture-&amp;gt;creer&#039;)), // Client Invoice&lt;br /&gt;
                      array(&#039;context&#039;=&amp;gt;&#039;membercard&#039;, &#039;table_element&#039;=&amp;gt;&#039;adherent&#039;, &#039;idvar&#039;=&amp;gt;&#039;rowid&#039;, &#039;rights&#039;=&amp;gt;array(&#039;$user-&amp;gt;rights-&amp;gt;adherent-&amp;gt;creer&#039;), &#039;tabs&#039;=&amp;gt;array(&#039;objecttype&#039;=&amp;gt;&#039;member_admin&#039;, &#039;function&#039;=&amp;gt;&#039;member_admin_prepare_head&#039;, &#039;lib&#039;=&amp;gt;DOL_DOCUMENT_ROOT.&#039;/core/lib/member.lib.php&#039;)), // Members / Adherents&lt;br /&gt;
                     ); // Edit me to add the support of another module - NOTE: Lowercase only!&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are also a lot of others parameters you can give to CustomFields so that the module gets better managed or to add a few other functionnalities, here is a non-exhaustive list:&lt;br /&gt;
* context: name of the module&#039;s context.&lt;br /&gt;
* table_element: name of the sql table that holds the object&#039;s datas.&lt;br /&gt;
* idvar: name of the variable that holds the id of the object in the sql database (by default &#039;id&#039; or &#039;rowid&#039;).&lt;br /&gt;
* rights: prevent the custom fields to be editable unless all the rights set here are enabled (preventing custom fields to be viewed is useless because they won&#039;t show anyway if the page can&#039;t be loaded due to insufficient rights, so this is directly handled by the module).&lt;br /&gt;
* tabs=&amp;gt;array(objecttype, function, lib, tabname, tabtitle): used to embed CustomFields&#039;s admin panel into another module&#039;s admin page.&lt;br /&gt;
&lt;br /&gt;
=== Configure creation triggers ===&lt;br /&gt;
&lt;br /&gt;
Add the triggers in $triggersarray:&lt;br /&gt;
$triggersarray = array(&amp;quot;order_create&amp;quot;=&amp;gt;&amp;quot;commande&amp;quot;,&lt;br /&gt;
                       &amp;quot;yourmodule_triggeraction1&amp;quot;=&amp;gt;&amp;quot;yourmodule_tablename&amp;quot;,&lt;br /&gt;
                       &amp;quot;yourmodule_triggeraction2&amp;quot;=&amp;gt;&amp;quot;yourmodule_tablename&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
Note: generally you will be looking to implement the _CREATE.&lt;br /&gt;
&lt;br /&gt;
Note2: if a triggeraction is not recognized by CustomFields (eg: mymodule_addpayment) then by default CustomFields will assimilate it to a _create action (it will create and save the values for the custom fields, just for a module&#039;s creation page).&lt;br /&gt;
&lt;br /&gt;
Note3: CustomFields also supports a few other triggers like _CLONE, _UPDATE, _MODIFY, but they are not required and may not work depending on how your module works to manage these actions.&lt;br /&gt;
&lt;br /&gt;
=== Configuration troubleshooting ===&lt;br /&gt;
&lt;br /&gt;
* If you cannot see the custom fields appear on your module&#039;s page, this means the hooks are not implemented or configured in CustomFields, please read the related chapters.&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet but there&#039;s no edit button icon: the module implements security but you did not configure it properly in CustomFields, please find the right security in $user-&amp;gt;rights and set it in conf_customfields.lib.php.&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the creation page but are not saved: you forgot to set the trigger in $triggersarray, or the creation trigger does not exist for this module and you have to implement it. Jump to [[Module_CustomFields_Dev#Triggers]].&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet but not on the creation page (or vice versa): A hook is missing on the page where the custom fields do not appear. Jump to [[Module_CustomFields_Dev#Hooks]].&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet with the edit button, but when the edit button is clicked, everything disappear: either the $idvar is not properly set (look at the URL to know if you need to set idvar), or the hook is not properly made ($object is not defined and it&#039;s called by another name, but the hook use $object instead of the correct variable).&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet with the edit button, can be edited but cannot be saved (the data is not saved): same as the previous point: either $idvar or $object bug.&lt;br /&gt;
&lt;br /&gt;
Also you might find useful to &#039;&#039;&#039;[[Module_CustomFields#General_advices_for_troubleshooting|enable php errors]]&#039;&#039;&#039; to better track errors if that&#039;s not already done.&lt;br /&gt;
&lt;br /&gt;
= Example cases =&lt;br /&gt;
&lt;br /&gt;
You can find a compilation of concrete, practical examples on the [[Module_CustomFields_Cases]] wiki page.&lt;br /&gt;
&lt;br /&gt;
= TO DO =&lt;br /&gt;
&lt;br /&gt;
You will find below the public list of features that should be implemented in the future.&lt;br /&gt;
&lt;br /&gt;
If you have an idea about a new feature that isn&#039;t listed below, feel free to add it to the list!&lt;br /&gt;
&lt;br /&gt;
== To Do ==&lt;br /&gt;
&lt;br /&gt;
* Better management of hidden fields with custom cascading (eg: works great on creation form, but on edit form and creation form with edit action, if the parents already have a value, the hidden fields will stay hidden! This is because of the AJAX is not called, and this is normal). -&amp;gt; Propose a better custom cascade function storing the hidden field&#039;s state inside extraoptions?&lt;br /&gt;
&lt;br /&gt;
== To Document ==&lt;br /&gt;
* Automatic recopy on cloning for custom fields and products&#039; lines custom fields.&lt;br /&gt;
* new API function: customfields_clone_or_recopy($object, $fromobject, $action2 = null)&lt;br /&gt;
&lt;br /&gt;
== Should do ==&lt;br /&gt;
* Add the possibility to mark a custom field to be shown in tables listings (eg: as a column in products listings) with &amp;quot;order by this column&amp;quot; buttons and stuffs. Will be difficult if the hooks are not yet implemented in listings.&lt;br /&gt;
* Checkbox (multiple checkboxes, one for each possible value) and multiselect (big select and can use SHIFT to multiselect) (store in XML file) (but impossible with constrained fields, can&#039;t store multiple foreign keys ids in one single sql field, but would work for other types like enum)&lt;br /&gt;
use fieldset, input checkbox and label for, eg:&lt;br /&gt;
    &amp;lt;fieldset&amp;gt;&amp;lt;legend&amp;gt;&amp;lt;label for=&amp;quot;cf_productslist&amp;quot;&amp;gt; $langs-&amp;gt;trans(&#039;cf_productslist&#039;)&amp;lt;/label&amp;gt;&amp;lt;/legend&amp;gt;&lt;br /&gt;
    &amp;lt;input type=&amp;quot;checkbox&amp;quot; name=&amp;quot;cf_productslist_choice1&amp;quot; value=&amp;quot;1&amp;quot; id=&amp;quot;cf_productslist_choice1&amp;quot; /&amp;gt;&amp;lt;label for=&amp;quot;cf_productslist_choice1&amp;quot;&amp;gt; $langs-&amp;gt;trans(&#039;cf_productslist_choice1&#039;)&amp;lt;/label&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
    &amp;lt;input type=&amp;quot;checkbox&amp;quot; name=&amp;quot;cf_productslist_choice2&amp;quot; value=&amp;quot;1&amp;quot; id=&amp;quot;cf_productslist_choice2&amp;quot; /&amp;gt;&amp;lt;label for=&amp;quot;cf_productslist_choice2&amp;quot;&amp;gt; $langs-&amp;gt;trans(&#039;cf_productslist_choice2&#039;)&amp;lt;/label&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
    etc...&lt;br /&gt;
    &amp;lt;/fieldset&amp;gt;&lt;br /&gt;
* Add a javascript options values generator for the enum type (a hidden input that would be shown only when DropdownBox type is selected and that would permit to add options by clicking a plus button).&lt;br /&gt;
* Replace triggers in customfields class by hookmanager?&lt;br /&gt;
* AJAX previsualisation of the HTML custom field that will be created.&lt;br /&gt;
* In ODTs, create special tags to directly access day, month or year value of a date type field independently of the rest.&lt;br /&gt;
* New overloading function&#039;s action: &amp;quot;get&amp;quot;, similar to view except that it works not only on Dolibarr&#039;s view but whenever someone tries to access this field&#039;s value.&lt;br /&gt;
* Add Upload and Image field types (this would allow for an easier integration inside ODT and PDF templates than the current workflow of Dolibarr).&lt;br /&gt;
&lt;br /&gt;
== Known bugs ==&lt;br /&gt;
* in product and service modules, if you edit a field, the proposals and other fields below won&#039;t be shown, you need to refresh the page. This problem resides in Dolibarr I think (since we are simply using a hook).&lt;br /&gt;
* ficheinter (Intervention module) custom fields will stay in create mode just after create page is submitted (this is because of the $action that is not standard - $action=edit or something).&lt;br /&gt;
&lt;br /&gt;
== Never/Maybe one day ==&lt;br /&gt;
* Add support for repeatable (predefined) invoices (the way it is currently managed makes it very difficult to manage this without making a big exception, adding specific functions in customfields modules that would not at all will be reusable anywhere else, when customfields has been designed to be as generic as possible to support any module and any version of dolibarr, because it&#039;s managed by a totally different table while it&#039;s still managed by the same module, CustomFields work with the paradigm: one module, one table).&lt;br /&gt;
* Add an AJAX select box for constrained values to automatically select the appropriate name for Smart Value Substitution : when a constrained type is selected and a table is selected, a hidden select box would show up with the list of the fields of this table to choose the values that will be printed as the values for this customfield (eg: for table llx_users you could select the &amp;quot;nom&amp;quot; field and then it would automatically prepend &amp;quot;nom_&amp;quot; to the field&#039;s name).&lt;br /&gt;
* Fine-grained rights management: rights per group, per user, and per field.&lt;br /&gt;
* Replace overloading functions (extend class) by hookmanager if possible (but how to simulate the *full switch? Plus this will force users to make their own modules to make those functions, and to disable/renable their modules everytime they will add a new function...).&lt;br /&gt;
* Refactor the trigger array: merge it with the modulesarray (along with a new way to specify which customfields trigger action a trigger should correspond, eg: &#039;linebill_insert&#039;=&amp;gt;&#039;customfields_create&#039;). But the triggers aren&#039;t necessarily associated with a specific module...&lt;br /&gt;
* AJAX autocompletion / auto population of field&#039;s content when typing the beginning of a value in a text box.&lt;/div&gt;</summary>
		<author><name>Lrq3000</name></author>
	</entry>
	<entry>
		<id>https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33348</id>
		<title>Module CustomFields Cases</title>
		<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33348"/>
		<updated>2015-04-01T14:22:58Z</updated>

		<summary type="html">&lt;p&gt;Lrq3000: /* Cascading */ Added images&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:CustomFields]]&lt;br /&gt;
{{TemplateDocDevEn}}&lt;br /&gt;
{{TemplateModEN}}&lt;br /&gt;
{{BasculeDevUserEn|&lt;br /&gt;
name=CustomFields|&lt;br /&gt;
num=8500|&lt;br /&gt;
devdoc=[[Module_CustomFields_Dev]]|&lt;br /&gt;
userdoc=[[Module_CustomFields]]|}}&lt;br /&gt;
This page lists a compilation of concrete, practical examples uses of [[Module_CustomFields|CustomFields]] and of its various features.&lt;br /&gt;
&lt;br /&gt;
If you had an experience with CustomFields, you can also add here your case as an example for others to get inspired.&lt;br /&gt;
&lt;br /&gt;
= How to make a generic PDF template listing all custom fields =&lt;br /&gt;
&lt;br /&gt;
To make a generic PDF template listing all custom fields, you just need to take an existing PDF template, then you need to follow these 3 steps:&lt;br /&gt;
&lt;br /&gt;
1/ Edit the class name (required, else it will conflict with the original template).&lt;br /&gt;
&lt;br /&gt;
Eg: if we take the crabe template for invoices, we change:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;crabe&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
into:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_customfields extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;customfields&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2/ Copy this generic function at the end of the PDF:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propale/order/etc... (CustomFields simpler functions will automatically adapt)&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs); // set default PDF font size&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
    // Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
    $customfields = customfields_fill_object($object, null, $outputlangs, null, true);&lt;br /&gt;
&lt;br /&gt;
    // Setting the starting position of the text cursor&lt;br /&gt;
    $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
    $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
    // Printing the customfields&lt;br /&gt;
    foreach ($object-&amp;gt;customfields as $label=&amp;gt;$value) { // $value is already formatted!&lt;br /&gt;
        // Get translated label&lt;br /&gt;
        $translatedlabel = $customfields-&amp;gt;findLabelPDF($label, $outputlangs); // translated label of the customfield (not translated by default in customfields_fill_object() because a field should always be accessible by a base name, whatever the translation is)&lt;br /&gt;
&lt;br /&gt;
        // PDF formatting, placement and printing&lt;br /&gt;
        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
        $pdf-&amp;gt;MultiCell(0,3, $translatedlabel.&#039; ($object-&amp;gt;customfields-&amp;gt;&#039;.$label.&#039;): &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternative function (with a bit less functionalities, but more controllable):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page (used to debug if CustomFields setup is correct)&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propal/product/whatever...&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs);&lt;br /&gt;
&lt;br /&gt;
            if (empty($object-&amp;gt;table_element) or empty($object-&amp;gt;id)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;Current \$object is not compatible with CustomFields, could not find table_element or id.&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
    $customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element);&lt;br /&gt;
&lt;br /&gt;
            // Fetching custom fields records&lt;br /&gt;
            $fields = $customfields-&amp;gt;fetch($object-&amp;gt;id);&lt;br /&gt;
&lt;br /&gt;
            if (!isset($fields)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;No custom field could be found for this object. Please check your configuration (did you create at least one customfield and set a value in the current datasheet?)&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            } else {&lt;br /&gt;
                // Setting the starting position of the text cursor&lt;br /&gt;
                $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
                $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
                // Printing the customfields&lt;br /&gt;
                foreach ($fields as $key=&amp;gt;$field) {&lt;br /&gt;
                        $translatedname = $customfields-&amp;gt;findLabelPDF($key, $outputlangs); // label of the customfield&lt;br /&gt;
                        $value = $customfields-&amp;gt;simpleprintFieldPDF($key, $field, $outputlangs); // value (cleaned and properly formatted) of the customfield&lt;br /&gt;
&lt;br /&gt;
                        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
                        $pdf-&amp;gt;MultiCell(0,3, $translatedname.&#039;: &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
                        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3/ Call the _pagecustomfields function at the right place (generally, after _pagefoot and just before $pdf-&amp;gt;Close()):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Pied de page&lt;br /&gt;
$this-&amp;gt;_pagefoot($pdf,$object,$outputlangs);&lt;br /&gt;
$pdf-&amp;gt;AliasNbPages();&lt;br /&gt;
&lt;br /&gt;
// CustomFields&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) { // if the customfields module is activated...&lt;br /&gt;
        // Add a page with the customfields&lt;br /&gt;
        $pdf-&amp;gt;AddPage();&lt;br /&gt;
        $this-&amp;gt;_pagecustomfields($pdf,$object,$outputlangs);&lt;br /&gt;
        $pagenb++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
$pdf-&amp;gt;Close(); // you should first search for this, you should only find one occurrence&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then go to the module&#039;s admin panel (eg: invoice admin panel) and enable the template you&#039;ve just created.&lt;br /&gt;
&lt;br /&gt;
You can now use it to test if your customfields work.&lt;br /&gt;
&lt;br /&gt;
= Conditions on fields selection =&lt;br /&gt;
&lt;br /&gt;
Sometimes you may want to show a DropdownBox or Constraint (or another multi-choice type), but you may want to show or hide choices based on conditions (eg: only if it&#039;s a prospect, only if this zone was selected, only if this category was selected, etc..).&lt;br /&gt;
&lt;br /&gt;
To know which is the best solution you should choose to implement your condition, you should first know in which category your condition falls in.&lt;br /&gt;
&lt;br /&gt;
== Conditions categories ==&lt;br /&gt;
&lt;br /&gt;
Conditions can be categorized into 4 types:&lt;br /&gt;
&lt;br /&gt;
* static condition: choices are restrained on a condition that never changes (eg: only show third-parties that are suppliers, or lowercase all data on sql insertion, or check that a field is above a certain number, etc.).&lt;br /&gt;
* semi-dynamic condition: choices that are constrained on a static table but based on the value of another field/customfield. This is also commonly called &amp;quot;Cascaded dropdown lists&amp;quot; or &amp;quot;Dynamically linked dropdown lists&amp;quot;.&lt;br /&gt;
* dynamic: everything is dynamic: choices are based on a subfield of a field of the current module, so that there&#039;s no material table and it&#039;s impossible to create a view (because you would need to create one view per id) (eg: show only the contacts attached to the third-party being accessed)&lt;br /&gt;
* timed condition: a condition that is based on time or recurrence (eg: at 5:00 AM delete old records, etc..)&lt;br /&gt;
&lt;br /&gt;
== Conditions solutions ==&lt;br /&gt;
&lt;br /&gt;
Now that you know what kind of condition you want to set, you can choose one of the following solutions depending on your preferences and knowledge.&lt;br /&gt;
&lt;br /&gt;
In any case, you can always use the &#039;&#039;&#039;CustomFields&#039;s overload functions&#039;&#039;&#039; to fit your needs for nearly every type of condition.&lt;br /&gt;
&lt;br /&gt;
=== Static condition ===&lt;br /&gt;
&lt;br /&gt;
* Constraint WHERE clause (when creating/editing a custom field, you can specify your own WHERE conditions).&lt;br /&gt;
* View: create a view on the table based on the condition, and then create a custom field constrained on this view (which is just like any table), eg: only show third-parties that are suppliers:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
CREATE VIEW llx_societe_supplier_view AS&lt;br /&gt;
SELECT *&lt;br /&gt;
FROM llx_societe&lt;br /&gt;
WHERE fournisseur=1;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
WARNING: foreign keys (and thus constraints) can&#039;t be used on views with MySQL, and materialized views neither exist, so you can&#039;t use this method with MySQL.&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
* SQL Transformations&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
=== Semi-dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade option is ideal in most cases, and if not enough, you can use the Cascade Custom option and Overloading Functions.&lt;br /&gt;
* View&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
&lt;br /&gt;
=== Dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade Custom and Overload functions&lt;br /&gt;
* Recursive Remote Fields Access&lt;br /&gt;
* Making your own module, which calls the CustomFields class&lt;br /&gt;
&lt;br /&gt;
=== Timed condition ===&lt;br /&gt;
&lt;br /&gt;
* SQL scheduled events&lt;br /&gt;
* Cron job&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
= Cascading =&lt;br /&gt;
&lt;br /&gt;
A simple example of [http://wiki.dolibarr.org/index.php/Module_CustomFields#Cascade cascading to select a location] can be found in the user manual.&lt;br /&gt;
&lt;br /&gt;
But you can do a lot more with cascading if you combine it with other options, such as Duplication and Hide. Here is an example:&lt;br /&gt;
&lt;br /&gt;
Let&#039;s say that in invoices, you want to be able to select a contact of the society selected for this invoice. Of course, you want that the list of contacts show only the contacts related to the society (ie: that you added on the Third-Party datasheet of this society).&lt;br /&gt;
&lt;br /&gt;
To do that, you have to proceed in two steps:&lt;br /&gt;
&lt;br /&gt;
1- Create a custom field &amp;quot;soccpy_nom&amp;quot; constrained on llx_society that will duplicate the society&#039;s id. This is necessary because other custom fields can only be cascaded using other custom fields, not any Dolibarr fields. Using duplication allows you to duplicate any Dolibarr field (and even $_GET and $_POST), so that you can use them to cascade. Copy the parameters as shown in this image:&lt;br /&gt;
&lt;br /&gt;
[[File:Cf-case-cascade-contacts1.png]]&lt;br /&gt;
&lt;br /&gt;
2- Create a custom field &amp;quot;contact_firstname_lastname&amp;quot; with Cascade option with parent &amp;quot;soccpy_nom&amp;quot; and constrained on the table llx_socpeople (the table of the contacts), which will show the list of contacts associated only to the selected society in soccpy_nom. Copy the parameters as shown in this image:&lt;br /&gt;
&lt;br /&gt;
[[File:Cf-case-cascade-contacts2.png]]&lt;br /&gt;
&lt;br /&gt;
And voila, you&#039;re done! The field soccpy_nom should be hidden so you won&#039;t see it, but you should see the field contact_firstname_lastname and you can check that only the contacts related to the society selected for the current invoice are shown.&lt;br /&gt;
&lt;br /&gt;
= Overloading functions =&lt;br /&gt;
&lt;br /&gt;
Here are a few concrete use cases of overloading functions to achieve various goals.&lt;br /&gt;
&lt;br /&gt;
== Dynamic list selection based on another one ==&lt;br /&gt;
&lt;br /&gt;
The goal is to show two custom fields on the Third-Party module: one which gives the zone (secteur) of the third-party (Isere, Alpes du sud, Haute-Savoie...) and the other which gives relative to the zone the list of all the ski resorts (station_a) inside the selected zone.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will show how to manually manage this kind of requirements using [[Module_CustomFields#Overloading_functions:_Adding_your_own_management_code_for_custom_fields|overloading functions]], but in latest CustomFields releases, the same solution can be achieved automatically using the [[Module_CustomFields#Cascade|Cascade option]].&lt;br /&gt;
&lt;br /&gt;
We have one table &#039;&#039;&#039;llx_k_station&#039;&#039;&#039; (rowid, station (char50), secteur(char50)) which contains the following: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1,les 2 alpes,isère&lt;br /&gt;
2,chamrousse,isère&lt;br /&gt;
3,alpe d&#039;huez,isère&lt;br /&gt;
4,vars,alpes du sud&lt;br /&gt;
5,risoul,alpes du sud&lt;br /&gt;
etc...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In CustomFields&#039;s admin panel, we create two custom fields:&lt;br /&gt;
&lt;br /&gt;
1- secteur: returns the list of all zones - type DropdownBox: enum(&#039;- Aucun&#039;,&#039;Alpes du Sud&#039;,&#039;export&#039;,&#039;Haute-Savoie&#039;,&#039;Isère&#039;, etc...)&lt;br /&gt;
&lt;br /&gt;
2- station_a: returns the list of all ski resorts - type constraint on &#039;&#039;&#039;llx_k_station&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Here is the code to put in customfields_fields_extend.lib.php that will allow to show only the ski resorts corresponding to the selected zone:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_editview_societe_station_a (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$parameters, &amp;amp;$action, &amp;amp;$user, &amp;amp;$idvar, &amp;amp;$rightok, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
  global $db; // allow to access database functions&lt;br /&gt;
  $sql=&amp;quot;SELECT llx_k_station.station, llx_k_station.rowid FROM llx_societe_customfields INNER JOIN llx_k_station ON llx_societe_customfields.Secteur = llx_k_station.secteur WHERE llx_societe_customfields.fk_societe=&amp;quot;.$object-&amp;gt;id;&lt;br /&gt;
$result=$db-&amp;gt;query($sql);&lt;br /&gt;
 &lt;br /&gt;
  if ($result) {&lt;br /&gt;
    $num = $db-&amp;gt;num_rows($result);&lt;br /&gt;
    $i = 0;&lt;br /&gt;
    $myarray = array();&lt;br /&gt;
    while ($i &amp;lt; $num)&lt;br /&gt;
    {&lt;br /&gt;
      $obj = $db-&amp;gt;fetch_object($result);&lt;br /&gt;
      $myarray []=array(&#039;id&#039;=&amp;gt;$obj-&amp;gt;rowid, &#039;value&#039;=&amp;gt;$obj-&amp;gt;station);&lt;br /&gt;
      $i++;&lt;br /&gt;
    }&lt;br /&gt;
    $value = $myarray; &lt;br /&gt;
    $db-&amp;gt;free($result);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to manub for giving this case.&lt;br /&gt;
&lt;br /&gt;
== Dynamic discount computation per products lines on propales ==&lt;br /&gt;
&lt;br /&gt;
The goal here is to compute the discount for a product line in propales based on a computation on three custom fields.&lt;br /&gt;
&lt;br /&gt;
First, create three custom fields for &amp;quot;Line commercial proposals&amp;quot;: vl, cpl and lc.&lt;br /&gt;
&lt;br /&gt;
Then add the following overloading function at the end of your customfields_fields_extend.lib.php file (read the comments for more informations):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_propaldet_vl (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
&lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $vl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;vl&#039;};&lt;br /&gt;
    $cpl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;cpl&#039;};&lt;br /&gt;
    $lc = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;lc&#039;};&lt;br /&gt;
&lt;br /&gt;
    // Compute the discount&lt;br /&gt;
    $remise_calculee = (100 - $vl) * (100 - $cpl) * (100 - $lc);&lt;br /&gt;
&lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $remise_calculee = price2num($remise_calculee);&lt;br /&gt;
&lt;br /&gt;
    // Assign the new discount into the PropalLine object&lt;br /&gt;
    $object-&amp;gt;remise_percent = $remise_calculee;&lt;br /&gt;
&lt;br /&gt;
    // Update the price (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
&lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
&lt;br /&gt;
    //$q = &amp;quot;UPDATE  &amp;quot;.MAIN_DB_PREFIX.&amp;quot;propaldet SET remise_percent=&amp;quot;.$remise_calculee.&amp;quot; WHERE  rowid=&amp;quot;.$object-&amp;gt;rowid;&lt;br /&gt;
    //$db-&amp;gt;query($q);&lt;br /&gt;
    &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;vl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;cpl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;lc&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Linking Dolibarr objects from two different modules =&lt;br /&gt;
&lt;br /&gt;
Dolibarr sometimes offers the possibility to link two different modules. Eg: when you convert an Intervention card into an Invoice.&lt;br /&gt;
&lt;br /&gt;
In this case, you might want in your templates to fetch the custom fields of the linked object. Eg: you generate a PDF from the Invoice, but you want to get the custom fields from the Intervention that was converted into the Invoice.&lt;br /&gt;
&lt;br /&gt;
The key here is to use the &#039;&#039;&#039;llx_element_element&#039;&#039;&#039; table or the &#039;&#039;&#039;llx_element_*&#039;&#039;&#039; tables (eg: llx_element_contact): this is a standard Dolibarr table that stores all links between objects of two different modules.&lt;br /&gt;
&lt;br /&gt;
Here is the structure:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
rowid | fk_source | sourcetype | fk_target | targettype&lt;br /&gt;
-------------------------------------------------------&lt;br /&gt;
1 | 2 | fichinter | 5 | facture&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can then use a php code like the following to populate your $object with customfields:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
// This will also create and returns us an instance of the CustomFields class, which we will use to manually query the llx_element_element table&lt;br /&gt;
$customfields = customfields_fill_object($object, null, $outputlangs, &#039;facture&#039;, true); // beautified values&lt;br /&gt;
&lt;br /&gt;
// Querying the llx_element_element table manually to get the link between the invoice and the linked intervention card (we want the id of the intervention card)&lt;br /&gt;
// fetchAny($columns, $table, $where=&#039;&#039;, $orderby=&#039;&#039;, $limitby=&#039;&#039;)&lt;br /&gt;
$eltrow = $customfields-&amp;gt;fetchAny(&#039;*&#039;, MAIN_DB_PREFIX.&#039;element_element&#039;, &#039;targettype=&amp;quot;&#039;.$object-&amp;gt;element.&#039;&amp;quot; and fk_target=&#039;.$object-&amp;gt;id.&#039; and sourcetype=&amp;quot;fichinter&amp;quot;&#039;);&lt;br /&gt;
&lt;br /&gt;
// Save the intervention&#039;s id&lt;br /&gt;
$sourceid = $eltrow[0][&#039;fk_source&#039;];&lt;br /&gt;
&lt;br /&gt;
// Crafting a dummy intervention object&lt;br /&gt;
$fromobject = new stdClass(); // Init an empty object&lt;br /&gt;
$fromobject-&amp;gt;id = $sourceid; // We need the id and..&lt;br /&gt;
$fromobject-&amp;gt;table_element = &#039;fichinter&#039;; // the table_element (name of the table)&lt;br /&gt;
&lt;br /&gt;
// We can then fill $object with the intervention custom fields (we store them in a sub property &#039;fichinter&#039; to avoid conflicts with invoice&#039;s custom fields)&lt;br /&gt;
customfields_fill_object($object, $fromobject, $outputlangs, &#039;fichinter&#039;, true);&lt;br /&gt;
&lt;br /&gt;
// Now you can access invoices custom fields using:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;facture-&amp;gt;cf_myfield);&lt;br /&gt;
// And access intervention custom fields with:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;fichinter-&amp;gt;cf_myfield);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to netassopro for the tip.&lt;br /&gt;
&lt;br /&gt;
= Modify the total global price with a coefficient (without tampering the discount) =&lt;br /&gt;
&lt;br /&gt;
This is more troublesome than modifying the total price per product/service line, because the total global price of an object (let&#039;s say an invoice, but this also works for any other object) is always recomputed by summing the prices and quantity of each product/service line. This means that although there&#039;s an entry &amp;quot;total_ttc&amp;quot; in the database to store the invoice&#039;s total price, this price is in fact always recomputed, it&#039;s never used as-is. Thus, it&#039;s useless to change only the database record: we need to inject some code to change how the total price is computed.&lt;br /&gt;
&lt;br /&gt;
This could be done using an overloading function or a trigger, but unluckily, Dolibarr doesn&#039;t call a trigger nor a hook when recomputing the price. We thus need to edit core files to do what we want.&lt;br /&gt;
&lt;br /&gt;
However, the bright side is that we can cleverly edit just two functions from one core file: update_price() and getMarginInfos() from /htdocs/core/class/commonobject.class.php , and everything should run smoothly. Beware that if you update Dolibarr, you will need to redo these two modifications in the code.&lt;br /&gt;
&lt;br /&gt;
First, you need to create a new custom field called &amp;quot;global_coefficient&amp;quot; in Invoices (not Invoices Lines!). Then you can continue to the subchapters below.&lt;br /&gt;
&lt;br /&gt;
Also note that this global coefficient is also compatible with per line coefficients (as described in the next chapter), thus you can use a global coefficient and per line coefficients at the same time. You can also use multiple global coefficient if you wish.&lt;br /&gt;
&lt;br /&gt;
Another note: if you have a grid of discounts depending on quantity or price or whatever parameter, you can program a precomputation code using a Custom Ajax function (this way, your discounts will be automatically computed, but then you can still manually enter them for particular cases since the custom field is editable).&lt;br /&gt;
&lt;br /&gt;
== Implement the change of global price in most of Dolibarr (including remaining debt to pay and PDF and ODT) ==&lt;br /&gt;
&lt;br /&gt;
The change we will implement here will reflect the new total price with the global coefficient in most of Dolibarr&#039;s code, since we will here change the main function that is usually called to update and get the total price.&lt;br /&gt;
&lt;br /&gt;
Open /htdocs/core/class/commonobject.class.php and look for the function update_price().&lt;br /&gt;
&lt;br /&gt;
You should see the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function update_price($exclspec=0,$roundingadjust=&#039;none&#039;,$nodatabaseupdate=0,$seller=&#039;&#039;)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just below the include_once line, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;;&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/conf/conf_customfields_func.lib.php&#039;;&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $this-&amp;gt;table_element);&lt;br /&gt;
// Get object&#039;s id (either stored in property &amp;quot;id&amp;quot; or &amp;quot;rowid&amp;quot;)&lt;br /&gt;
$this_id = (!empty($this-&amp;gt;id)) ? $this-&amp;gt;id : $this-&amp;gt;rowid;&lt;br /&gt;
// Load custom fields records&lt;br /&gt;
$cf_parent_record = $customfields-&amp;gt;fetch($this_id);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will load the global_coefficient custom field (and also any other custom field you have defined for invoices).&lt;br /&gt;
&lt;br /&gt;
Now scroll below, and you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
while ($i &amp;lt; $num)&lt;br /&gt;
{&lt;br /&gt;
    $obj = $this-&amp;gt;db-&amp;gt;fetch_object($resql);&lt;br /&gt;
&lt;br /&gt;
    ... (lots of code)&lt;br /&gt;
&lt;br /&gt;
    i++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Add revenue stamp to total&lt;br /&gt;
$this-&amp;gt;total_ttc       += isset($this-&amp;gt;revenuestamp)?$this-&amp;gt;revenuestamp:0;&lt;br /&gt;
&lt;br /&gt;
$this-&amp;gt;db-&amp;gt;free($resql);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is the end of the loop that is summing each product/service line price to compute the total price. Just after this loop, you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Now update global field total_ht, total_ttc and tva&lt;br /&gt;
$fieldht=&#039;total_ht&#039;;&lt;br /&gt;
$fieldtva=&#039;tva&#039;;&lt;br /&gt;
$fieldlocaltax1=&#039;localtax1&#039;;&lt;br /&gt;
$fieldlocaltax2=&#039;localtax2&#039;;&lt;br /&gt;
$fieldttc=&#039;total_ttc&#039;;&lt;br /&gt;
// Specific code for backward compatibility with old field names&lt;br /&gt;
if ($this-&amp;gt;element == &#039;facture&#039; || $this-&amp;gt;element == &#039;facturerec&#039;)             $fieldht=&#039;total&#039;;&lt;br /&gt;
if ($this-&amp;gt;element == &#039;facture_fourn&#039; || $this-&amp;gt;element == &#039;invoice_supplier&#039;) $fieldtva=&#039;total_tva&#039;;&lt;br /&gt;
if ($this-&amp;gt;element == &#039;propal&#039;)                                                $fieldttc=&#039;total&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Between those two blocks of code (so just above // Now update global field total_ht, total_ttc and tva), you can place the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
if (!empty($cf_parent_record-&amp;gt;global_coefficient)) { // avoid applying the coefficient if empty (this will return a 0 price)&lt;br /&gt;
    $this-&amp;gt;total_ht = $this-&amp;gt;total_ht * $cf_parent_record-&amp;gt;global_coefficient;&lt;br /&gt;
    $this-&amp;gt;total_tva = $this-&amp;gt;total_tva * $cf_parent_record-&amp;gt;global_coefficient;&lt;br /&gt;
    $this-&amp;gt;total_ttc = $this-&amp;gt;total_ttc * $cf_parent_record-&amp;gt;global_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Done, now try to add/edit a product/service line, and the total price should be affected by the global coefficient.&lt;br /&gt;
&lt;br /&gt;
Note that this modification &#039;&#039;&#039;needs only to be done once for all modules&#039;&#039;&#039;, you then just have to create a custom field named &amp;quot;global_coefficient&amp;quot; in every module you want to apply this coefficient.&lt;br /&gt;
&lt;br /&gt;
== Implementing the change of price in margin module ==&lt;br /&gt;
&lt;br /&gt;
If you enabled the margin module, you should see that the coefficient is not applied there, but it&#039;s easy to fix that.&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/class/commonobject.class.php and find the function getMarginInfos(), then inside find the following lines:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach($this-&amp;gt;lines as $line) {&lt;br /&gt;
    if (empty($line-&amp;gt;pa_ht) &amp;amp;&amp;amp; isset($line-&amp;gt;fk_fournprice) &amp;amp;&amp;amp; !$force_price) {&lt;br /&gt;
        $product = new ProductFournisseur($this-&amp;gt;db);&lt;br /&gt;
        if ($product-&amp;gt;fetch_product_fournisseur_price($line-&amp;gt;fk_fournprice))&lt;br /&gt;
            $line-&amp;gt;pa_ht = $product-&amp;gt;fourn_unitprice * (1 - $product-&amp;gt;fourn_remise_percent / 100);&lt;br /&gt;
        if (isset($conf-&amp;gt;global-&amp;gt;MARGIN_TYPE) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;MARGIN_TYPE == &amp;quot;2&amp;quot; &amp;amp;&amp;amp; $product-&amp;gt;fourn_unitcharges &amp;gt; 0)&lt;br /&gt;
            $line-&amp;gt;pa_ht += $product-&amp;gt;fourn_unitcharges;&lt;br /&gt;
    }&lt;br /&gt;
    // si prix d&#039;achat non renseigné et devrait l&#039;être, alors prix achat = prix vente&lt;br /&gt;
    if ((!isset($line-&amp;gt;pa_ht) || $line-&amp;gt;pa_ht == 0) &amp;amp;&amp;amp; $line-&amp;gt;subprice &amp;gt; 0 &amp;amp;&amp;amp; (isset($conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull == 1)) {&lt;br /&gt;
        $line-&amp;gt;pa_ht = $line-&amp;gt;subprice * (1 - ($line-&amp;gt;remise_percent / 100));&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the lines are filled with margin infos. The price has just been fetched here, and we will now modify it with the coefficient before the price gets used for margin computation.&lt;br /&gt;
&lt;br /&gt;
To optimize things up, we will load the custom fields before the loop (because they don&#039;t change between product/service lines, they are the custom fields of the parent object), and then apply the global_coefficient inside the loop.&lt;br /&gt;
&lt;br /&gt;
Just above the code we found (just before the for loop), paste the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
// Get object&#039;s id (either stored in property &amp;quot;id&amp;quot; or &amp;quot;rowid&amp;quot;)&lt;br /&gt;
$this_id = (!empty($this-&amp;gt;id)) ? $this-&amp;gt;id : $this-&amp;gt;rowid;&lt;br /&gt;
// Load custom fields records&lt;br /&gt;
$customfields = customfields_fill_object($this);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And now, just below the code we found, inside the loop (and just above the comment: &amp;quot;// calcul des marges&amp;quot;), paste the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
// Apply the coefficient&lt;br /&gt;
if (!empty($this-&amp;gt;customfields-&amp;gt;cf_global_coefficient)) {&lt;br /&gt;
    $line-&amp;gt;pa_ht = $line-&amp;gt;pa_ht * $this-&amp;gt;customfields-&amp;gt;cf_global_coefficient;&lt;br /&gt;
    $line-&amp;gt;subprice = $line-&amp;gt;subprice * $this-&amp;gt;customfields-&amp;gt;cf_global_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that here we cheated a bit: we do not compute the coefficient on the global total price, but rather per product/service line price. This is done on purpose, because mathematically this should produce no difference, and it also allows Dolibarr to continue peacefully with its margin calculations (else we would have to adapt all those computations, which would imply a lot more of code changes!).&lt;br /&gt;
&lt;br /&gt;
That&#039;s all, the price should be reflected in the margin. Just refresh the page to reflect the change.&lt;br /&gt;
&lt;br /&gt;
Note that this modification &#039;&#039;&#039;needs only to be done once for all modules&#039;&#039;&#039;, you then just have to create a custom field named &amp;quot;global_coefficient&amp;quot; in every module you want to apply this coefficient.&lt;br /&gt;
&lt;br /&gt;
== Refresh the price instantly ==&lt;br /&gt;
&lt;br /&gt;
Maybe you noticed one glitch: when you modify the global_coefficient, the global total price isn&#039;t updated until you add/edit a product/service line. This is because the update_price() function is only called when we add/edit a product/service line.&lt;br /&gt;
&lt;br /&gt;
To fix that, we need now to force the refreshing of price. To do that, we will use an &amp;quot;aftersave&amp;quot; overloading function, which will execute commands after our custom field is saved.&lt;br /&gt;
&lt;br /&gt;
Go to the folder /htdocs/customfields/fields and rename the file customfields_fields_extend.lib.php.example into customfields_fields_extend.lib.php .&lt;br /&gt;
&lt;br /&gt;
Now, open it in your favourite text editor, and add the following function :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_aftersave_facture_global_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value, $fields) {&lt;br /&gt;
    // Force refreshing total price&lt;br /&gt;
    $object-&amp;gt;update_price(0, &#039;auto&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s all. Now, when you edit your global_coefficient, this will force refresh the total price.&lt;br /&gt;
&lt;br /&gt;
However, you will still notice one last glitch: when you edit the global_coefficient, the total price isn&#039;t reflected instantly, you must refresh the webpage once to see the new price. This is because of how the datasheet and the CustomFields hook are designed: CustomFields is called only exactly where the custom fields are place, thus it is called after the total price, the margin and the remaining debt are printed. Thus, CustomFields updates the value of your custom field only after all those informations get printed, and thus it cannot modify them at this stage. That&#039;s why those information are out-of-date, and you must refresh the webpage once to get to see the latest values.&lt;br /&gt;
&lt;br /&gt;
There is a workaround for this: refresh automatically the webpage (but delete the POST data to avoid an infinite loop). You just need to use this overloading code instead of the one above:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_aftersave_facture_global_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value, $fields) {&lt;br /&gt;
    // Force refreshing total price&lt;br /&gt;
    $object-&amp;gt;update_price(0, &#039;auto&#039;);&lt;br /&gt;
    // Force refresh the page to update prices that were printed before the custom field aftersave was triggered (and only if not creating the object because then there&#039;s no need to refresh)&lt;br /&gt;
    if ($action !== &#039;add&#039;) print(&#039;&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;window.location.href = window.location.pathname + window.location.search;&amp;lt;/script&amp;gt;&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Another way to fix this issue would be to modify how CustomFields save the custom fields, by moving this stage at the very top. However, this would need another hook, which should be called at the very beginning of the page loading, which should be made only for this purpose (PageLoading), and should be available in all modules supported by CustomFields in order to not break anything.&lt;br /&gt;
&lt;br /&gt;
Note that you will need to create one overloading function for each module you want to support with the global_coefficient custom field. For example, if you want to support both invoices and commercial proposals, you will need two overloading functions:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient for invoices (note the &amp;quot;facture&amp;quot; in the name of the function)&lt;br /&gt;
function customfields_field_aftersave_facture_global_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value, $fields) {&lt;br /&gt;
    // Force refreshing total price&lt;br /&gt;
    $object-&amp;gt;update_price(0, &#039;auto&#039;);&lt;br /&gt;
    // Force refresh the page to update prices that were printed before the custom field aftersave was triggered (and only if not creating the object because then there&#039;s no need to refresh)&lt;br /&gt;
    if ($action !== &#039;add&#039;) print(&#039;&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;window.location.href = window.location.pathname + window.location.search;&amp;lt;/script&amp;gt;&#039;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Global coefficient for commercial proposals (note the &amp;quot;propal&amp;quot; in the name of the function)&lt;br /&gt;
function customfields_field_aftersave_propal_global_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value, $fields) {&lt;br /&gt;
    // Force refreshing total price&lt;br /&gt;
    $object-&amp;gt;update_price(0, &#039;auto&#039;);&lt;br /&gt;
    // Force refresh the page to update prices that were printed before the custom field aftersave was triggered (and only if not creating the object because then there&#039;s no need to refresh)&lt;br /&gt;
    if ($action !== &#039;add&#039;) print(&#039;&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;window.location.href = window.location.pathname + window.location.search;&amp;lt;/script&amp;gt;&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Modify the total price PER product/service line with a coefficient (without tampering the discount) =&lt;br /&gt;
&lt;br /&gt;
Here we will go through the creation and management of a custom field called &#039;&#039;&#039;coefficient&#039;&#039;&#039; that will change the total price of each product by modifying it by the coefficient.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will not create a coefficient per product but rather per product&#039;s line. Thus if you want to create a coefficient that never change between invoices but only depending on the product and stays static, please refer to the next chapter below about creating a custom tax per product.&lt;br /&gt;
&lt;br /&gt;
The preliminary and necessary step is to first create a custom field that will be used to modify the total price. We will create one for Client Invoice Lines.&lt;br /&gt;
&lt;br /&gt;
Go to the CustomFields administration panel, then into the Client Invoice Lines tab, then click on the button New Field, name the field &#039;&#039;&#039;coefficient&#039;&#039;&#039;, and set the type to &#039;&#039;&#039;Double&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
When you go to client invoices in Dolibarr, you should then see something similar to this:&lt;br /&gt;
[[File:Cfe1.jpg]]&lt;br /&gt;
You can see that our coefficient field was successfully created.&lt;br /&gt;
&lt;br /&gt;
Then here are two alternative ways of implementing the change of the total price of each product relativery to this coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change total price in database ==&lt;br /&gt;
&lt;br /&gt;
Here we will use a &amp;quot;save&amp;quot; overloading function to change the total price directly in the database, thus we won&#039;t have to modify anything else (eg: nor PDF templating nor ODT) since the total price will be already updated in the database.&lt;br /&gt;
&lt;br /&gt;
Warning: this method will work only with CustomFields =&amp;gt; 3.2.23 since the &amp;quot;save&amp;quot; overloading function did not work for products lines before.&lt;br /&gt;
&lt;br /&gt;
Just add the following overloading function in your customfields_fields_extend.lib.php file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_facturedet_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
 &lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $coefficient = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;coefficient&#039;}; // you could also use $coefficient = $value;&lt;br /&gt;
 &lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $coefficient = price2num($coefficient);&lt;br /&gt;
 &lt;br /&gt;
    // Get the original subprices (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
 &lt;br /&gt;
    // Update the subprices with the coefficient&lt;br /&gt;
    $object-&amp;gt;total_ht = $object-&amp;gt;total_ht * $coefficient; // compute no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva = $object-&amp;gt;total_tva * $coefficient; // compute tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc * $coefficient; // compute tax included price&lt;br /&gt;
 &lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(&#039;&#039;,true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
 &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;coefficient&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should show the correct total price on the Dolibarr&#039;s interface without modifying the unit subprice (P.U. Unit):&lt;br /&gt;
&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
You have nothing to edit in your PDF or ODT template, since the new subprice with applied coefficient will be automatically printed instead of the old one without coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change printed total price per product without tampering the subprice in the database ==&lt;br /&gt;
&lt;br /&gt;
Here is a way to change the printed total price (the one shown on screen and in PDF/ODT) but without changing the total price in the database.&lt;br /&gt;
&lt;br /&gt;
The following sub-chapters are all independent, thus you can only follow the ones that you require, eg: if you only need to change the price in your PDF documents, just create a custom field then jump to the sub-chapter to &#039;&#039;&#039;Implement the change of price in PDF&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in ODT templates ===&lt;br /&gt;
&lt;br /&gt;
There&#039;s no eval implementation currently inside ODT templates, but there is a kind of hook implementation, called [[Create_an_ODT_document_template#Other_personalized_tags|substitution functions]].&lt;br /&gt;
&lt;br /&gt;
You can either create your own module, then create your own substitution function, but for the sake of simplicity in this tutorial, we will directly use the CustomFields substitution function. However, please remember that you will have to redo your changes everytime you will update the CustomFields module, so if you don&#039;t want to do that it would be better if you create your own module and substitution function.&lt;br /&gt;
&lt;br /&gt;
Open the file &#039;&#039;&#039;htdocs/customfields/core/substitutions/functions_customfields.lib.php&#039;&#039;&#039; and go to the function &#039;&#039;&#039;customfields_completesubstitutionarray_lines&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
At the very end of this function, but inside the customfields check:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) {&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
But just before the ending bracket, you should add the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($object-&amp;gt;table_element_line === &#039;facturedet&#039;) { // First check that we are in the right module, we don&#039;t want to use coefficient for other modules if we didn&#039;t define the custom field there&lt;br /&gt;
    $substitutionarray[&#039;line_price_full&#039;] = $substitutionarray[&#039;line_price_ttc&#039;] * $substitutionarray[&#039;cf_coefficient&#039;]; // multiply the total price with our coefficient&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code will add a new tag &#039;&#039;&#039;{line_price_full}&#039;&#039;&#039; that you can then use inside your ODT templates.&lt;br /&gt;
&lt;br /&gt;
Just place this tag in a table in your ODT document, between the table tags ( [!-- BEGIN row.lines --] ... [!-- END row.lines --] ), and this will print your full price.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in PDF templates ===&lt;br /&gt;
&lt;br /&gt;
First, we will create our own PDF template by copying an existing template, and then we will edit it to change the price of items.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Creating your own PDF template by copying an existing template&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Go into the following folder: &#039;&#039;&#039;htdocs/core/modules/facture/doc/&#039;&#039;&#039;, then copy the file &#039;&#039;&#039;pdf_crabe.modules.php&#039;&#039;&#039; and name the new one &#039;&#039;&#039;pdf_crabecoeff.modules.php&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Then open it in your favourite editor, and change the following lines:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures // change pdf_crabe into pdf_crabecoeff&lt;br /&gt;
{&lt;br /&gt;
...&lt;br /&gt;
    function __construct($db)&lt;br /&gt;
    {&lt;br /&gt;
        ...&lt;br /&gt;
	$this-&amp;gt;name = &amp;quot;crabe&amp;quot;; // change &amp;quot;crabe&amp;quot; into &amp;quot;crabecoeff&amp;quot;&lt;br /&gt;
        ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you have your own PDF template!&lt;br /&gt;
&lt;br /&gt;
Now you should &#039;&#039;&#039;enable your PDF template&#039;&#039;&#039; before doing anything else, by going into the Modules Administration panel, and into the Invoice admin page, then click on the ON button at the right of the &#039;&#039;&#039;crabecoeff&#039;&#039;&#039; entry to enable the template.&lt;br /&gt;
&lt;br /&gt;
Now, let&#039;s proceed onto modifying the prices.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Changing the prices automatically in your PDF&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
You can now do pretty much anything you want in your PDF template, but I will give you a mean to automatically change the prices of every products with our coefficient, as well as the total price, without editing too much the template.&lt;br /&gt;
&lt;br /&gt;
First, try to find the following code of line (should be inside the write_file() method):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf=pdf_getInstance($this-&amp;gt;format);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, just copy/paste the following code &#039;&#039;&#039;below&#039;&#039;&#039; pdf_getInstance():&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$this-&amp;gt;customfields = customfields_fill_object($object, null, $outputlangs, null, true); // beautified values&lt;br /&gt;
$this-&amp;gt;customfields_raw = customfields_fill_object($object, null, $outputlangs, &#039;raw&#039;, null); // raw values&lt;br /&gt;
$this-&amp;gt;customfields_lines = customfields_fill_object_lines($object, null, $outputlangs, null, true); // product lines&#039; values&lt;br /&gt;
&lt;br /&gt;
// Resetting every $object price because we will compute them anew&lt;br /&gt;
$object-&amp;gt;total_ht = 0;&lt;br /&gt;
$object-&amp;gt;total_tva = 0;&lt;br /&gt;
$object-&amp;gt;total_ttc = 0;&lt;br /&gt;
// For each line of product/service, we will recompute all the prices&lt;br /&gt;
foreach($object-&amp;gt;lines as $line) {&lt;br /&gt;
    // Get the coefficient custom field&#039;s value for the current line&lt;br /&gt;
    $coeff = $object-&amp;gt;customfields-&amp;gt;lines-&amp;gt;{$line-&amp;gt;rowid}-&amp;gt;cf_coefficient;&lt;br /&gt;
    // Recompute the price of this product/service line&lt;br /&gt;
    $line-&amp;gt;total_ht = $line-&amp;gt;total_ht*$coeff; // compute no tax price&lt;br /&gt;
    $line-&amp;gt;total_tva = $line-&amp;gt;total_tva*$coeff; // compute tax amount&lt;br /&gt;
    $line-&amp;gt;total_ttc = $line-&amp;gt;total_ttc*$coeff; // compute tax included price&lt;br /&gt;
    // Do the sum of all new products/services prices and tva (will be incremented with every product/service line)&lt;br /&gt;
    $object-&amp;gt;total_ht += $line-&amp;gt;total_ht; // global no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva += $line-&amp;gt;total_tva; // global vat/tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc += $line-&amp;gt;total_ttc; // global  tax included price&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you should try to generate your PDF document, and you should get something like this:&lt;br /&gt;
[[File:Cfe3.jpg]]&lt;br /&gt;
&lt;br /&gt;
Note: printing the coefficient as a column in your PDF document is also possible but will not be covered here because PDF templates will vary for every persons depending on your needs.&lt;br /&gt;
For guidelines about how to do this, you can read the documentation on [[Create_a_PDF_document_template|how to create a PDF template]] and [[Module_CustomFields#Implementing_in_PDF_templates|how to use custom fields in PDF templates]].&lt;br /&gt;
&lt;br /&gt;
Hint: you should be looking inside the following conditional block, inside the write_file() method:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
{&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of total price in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
Unluckily there is not (yet) any hooking context to modify how lines of products/services are printed, thus the only thing you can do is directly edit the &#039;&#039;&#039;template file&#039;&#039;&#039; and manually put inside your code to load your custom field and print it the way you want using HTML formatting, for our example:&lt;br /&gt;
&lt;br /&gt;
* For Dolibarr 3.3 and above, edit the file &#039;&#039;&#039;htdocs/core/tpl/objectline_view.tpl.php&#039;&#039;&#039;&lt;br /&gt;
* For Dolibarr 3.1 and 3.2, edit the file &#039;&#039;&#039;htdocs/core/tpl/freeproductline_view.tpl.php&#039;&#039;&#039; AND &#039;&#039;&#039;predefinedproductline_view.tpl.php&#039;&#039;&#039; (apply the same changes to both files)&lt;br /&gt;
&lt;br /&gt;
In the template file you opened, find the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And replace it by:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $object-&amp;gt;table_element_line; // you can use $object-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
&lt;br /&gt;
// Modifying the HT price and printing it on the Dolibarr&#039;s datasheet interface&lt;br /&gt;
// Applying a multiplying/dividing coefficient to an HT price will give a mathematically correct TTC price since the taxes are also a multiplier&lt;br /&gt;
// However, if you want to apply other mathematical operations (like addition or substaction), you should use the total price with $line-&amp;gt;total_ttc&lt;br /&gt;
if (!empty($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
    echo price($line-&amp;gt;total_ht * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient);&lt;br /&gt;
} else {&lt;br /&gt;
    echo price($line-&amp;gt;total_ht);&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should produce something like this:&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Technical note: here we are using customfields_fill_object() instead of customfields_fill_object_lines() (the latter being specifically adapted to lines and thus should theoretically be used here) for performance reasons because the template file we are editing is called in a loop, and customfields_fill_object_lines() should be called outside a loop (because it already does the job that is done by the loop), so here we &#039;tricks&#039; the customfields_fill_object() function to avoid this loop by submitting &#039;facturedet&#039; as a module by itself (when in fact it&#039;s a product lines table, but it works the same way nevertheless). This is a good demonstration of the flexibility of the CustomFields functions and methods.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the coefficient column in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
It&#039;s also possible to show the coefficient in a new column on the Dolibarr interface.&lt;br /&gt;
&lt;br /&gt;
To do that, you need to do two things:&lt;br /&gt;
&lt;br /&gt;
1- Add the column title &amp;quot;Coeff.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
2- Add the generator that will input the coefficient value for each line.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Add the column title &amp;quot;Coeff.&amp;quot;&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
Open in your favourite editor the file /htdocs/core/class/commonobject.class.php and inside the function printObjectLines (note the plural s), find the following lines of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Total HT&lt;br /&gt;
print &#039;&amp;lt;td align=&amp;quot;right&amp;quot; width=&amp;quot;50&amp;quot;&amp;gt;&#039;.$langs-&amp;gt;trans(&#039;TotalHTShort&#039;).&#039;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is what prints the Total HT price column. We will place the Coeff column just before.&lt;br /&gt;
&lt;br /&gt;
So, just above this code, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
dol_include_once(&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element_line);&lt;br /&gt;
$coeff_field = $customfields-&amp;gt;fetchFieldStruct(&#039;coefficient&#039;);&lt;br /&gt;
if (!empty($coeff_field)) {&lt;br /&gt;
    print &#039;&amp;lt;td align=&amp;quot;right&amp;quot; width=&amp;quot;50&amp;quot;&amp;gt;&#039;.$langs-&amp;gt;trans(&#039;Coeff.&#039;).&#039;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will take care of showing the Coeff. column, but only for modules where you created such a custom field (because the file we modified is a common object for all modules in Dolibarr, not just invoices, thus this modification will gracefully skip this custom field if it does not exists for other modules).&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Add the generator&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/tpl/objectline_view.tpl.php and try to find the following block of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php if ($line-&amp;gt;special_code == 3)	{ ?&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo $langs-&amp;gt;trans(&#039;Option&#039;); ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } else { ?&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is what prints the total HT price value for each line.&lt;br /&gt;
&lt;br /&gt;
Just above of this block of code, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $object-&amp;gt;table_element_line; // you can use $this-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
if (isset($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
    &amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient; ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just like for the column title, this code will take care of generating the coefficient value only if the custom field is defined, because this file is also a generic template for all modules of Dolibarr.&lt;br /&gt;
&lt;br /&gt;
At this point, everything is done, you should see the following:&lt;br /&gt;
[[File:Cf_wiki_coeff_column.png]]&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in margin module ===&lt;br /&gt;
&lt;br /&gt;
If you enabled the margin module, you should see that the coefficient is not applied there, but it&#039;s easy to fix that.&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/class/commonobject.class.php and find the function getMarginInfos(), then inside find the following lines:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach($this-&amp;gt;lines as $line) {&lt;br /&gt;
    if (empty($line-&amp;gt;pa_ht) &amp;amp;&amp;amp; isset($line-&amp;gt;fk_fournprice) &amp;amp;&amp;amp; !$force_price) {&lt;br /&gt;
        $product = new ProductFournisseur($this-&amp;gt;db);&lt;br /&gt;
        if ($product-&amp;gt;fetch_product_fournisseur_price($line-&amp;gt;fk_fournprice))&lt;br /&gt;
            $line-&amp;gt;pa_ht = $product-&amp;gt;fourn_unitprice * (1 - $product-&amp;gt;fourn_remise_percent / 100);&lt;br /&gt;
        if (isset($conf-&amp;gt;global-&amp;gt;MARGIN_TYPE) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;MARGIN_TYPE == &amp;quot;2&amp;quot; &amp;amp;&amp;amp; $product-&amp;gt;fourn_unitcharges &amp;gt; 0)&lt;br /&gt;
            $line-&amp;gt;pa_ht += $product-&amp;gt;fourn_unitcharges;&lt;br /&gt;
    }&lt;br /&gt;
    // si prix d&#039;achat non renseigné et devrait l&#039;être, alors prix achat = prix vente&lt;br /&gt;
    if ((!isset($line-&amp;gt;pa_ht) || $line-&amp;gt;pa_ht == 0) &amp;amp;&amp;amp; $line-&amp;gt;subprice &amp;gt; 0 &amp;amp;&amp;amp; (isset($conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull == 1)) {&lt;br /&gt;
        $line-&amp;gt;pa_ht = $line-&amp;gt;subprice * (1 - ($line-&amp;gt;remise_percent / 100));&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the lines are filled with margin infos. The price has just been fetched here, and we will now modify it with the coefficient before the price gets used for margin computation.&lt;br /&gt;
&lt;br /&gt;
Just below the code we found, paste the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $line-&amp;gt;table_element;&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
if (!empty($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
    $line-&amp;gt;pa_ht = $line-&amp;gt;pa_ht * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient;&lt;br /&gt;
    $line-&amp;gt;subprice = $line-&amp;gt;subprice * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s all, the price should be reflected in the margin.&lt;br /&gt;
[[File:Cf_wiki_coeff_margin.png]]&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in remaining debt to pay ===&lt;br /&gt;
&lt;br /&gt;
Open /htdocs/core/class/commonobject.class.php and look for the function update_price().&lt;br /&gt;
&lt;br /&gt;
You should see the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function update_price($exclspec=0,$roundingadjust=&#039;none&#039;,$nodatabaseupdate=0,$seller=&#039;&#039;)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just below the include_once line, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;;&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/conf/conf_customfields_func.lib.php&#039;;&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $this-&amp;gt;table_element_line);&lt;br /&gt;
// Fetching line ids from parent object id&lt;br /&gt;
$line_ids = $customfields-&amp;gt;fetchAny(&#039;rowid&#039;, MAIN_DB_PREFIX.$this-&amp;gt;table_element_line, $this-&amp;gt;fk_element.&#039; = &#039;.$this-&amp;gt;id);&lt;br /&gt;
$line_ids = array_values_recursive(&#039;rowid&#039;, $line_ids);&lt;br /&gt;
// Load custom fields&lt;br /&gt;
$cf_records = $customfields-&amp;gt;fetch($line_ids);&lt;br /&gt;
// Reassociate the records with the line id (instead of the customfields record id)&lt;br /&gt;
$cf_line_cfrowid_to_rowid = array_reassociate(&#039;rowid&#039;, &#039;fk_&#039;.$this-&amp;gt;table_element_line, $cf_records);&lt;br /&gt;
$tmp = $cf_records;&lt;br /&gt;
$cf_records = array();&lt;br /&gt;
foreach ($tmp as $k=&amp;gt;$v) { $cf_records[$cf_line_cfrowid_to_rowid[$k]] = $tmp[$k]; }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will load the coefficient custom field (and also any other custom field you have defined for lines).&lt;br /&gt;
&lt;br /&gt;
Now scroll below, and you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
while ($i &amp;lt; $num)&lt;br /&gt;
{&lt;br /&gt;
    $obj = $this-&amp;gt;db-&amp;gt;fetch_object($resql);&lt;br /&gt;
&lt;br /&gt;
    // Note: There is no check on detail line and no check on total, if $forcedroundingmode = &#039;none&#039;&lt;br /&gt;
&lt;br /&gt;
    if ($forcedroundingmode == &#039;0&#039;)	// Check if data on line are consistent. This may solve lines that were not consistent because set with $forcedroundingmode=&#039;auto&#039;&lt;br /&gt;
    {&lt;br /&gt;
        $localtax_array=array($obj-&amp;gt;localtax1_type,$obj-&amp;gt;localtax1_tx,$obj-&amp;gt;localtax2_type,$obj-&amp;gt;localtax2_tx);&lt;br /&gt;
        $tmpcal=calcul_price_total($obj-&amp;gt;qty, $obj-&amp;gt;up, $obj-&amp;gt;remise_percent, $obj-&amp;gt;vatrate, $obj-&amp;gt;localtax1_tx, $obj-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $obj-&amp;gt;info_bits, $obj-&amp;gt;product_type, $seller, $localtax_array);&lt;br /&gt;
        $diff=price2num($tmpcal[1] - $obj-&amp;gt;total_tva, &#039;MT&#039;, 1);&lt;br /&gt;
        if ($diff)&lt;br /&gt;
        {&lt;br /&gt;
            $sqlfix=&amp;quot;UPDATE &amp;quot;.MAIN_DB_PREFIX.$this-&amp;gt;table_element_line.&amp;quot; SET &amp;quot;.$fieldtva.&amp;quot; = &amp;quot;.$tmpcal[1].&amp;quot;, total_ttc = &amp;quot;.$tmpcal[2].&amp;quot; WHERE rowid = &amp;quot;.$obj-&amp;gt;rowid;&lt;br /&gt;
            dol_syslog(&#039;We found unconsistent data into detailed line (difference of &#039;.$diff.&#039;) for line rowid = &#039;.$obj-&amp;gt;rowid.&amp;quot; (total vat of line calculated=&amp;quot;.$tmpcal[1].&amp;quot;, database=&amp;quot;.$obj-&amp;gt;total_tva.&amp;quot;). We fix the total_vat and total_ttc of line by running sqlfix = &amp;quot;.$sqlfix);&lt;br /&gt;
            $resqlfix=$this-&amp;gt;db-&amp;gt;query($sqlfix);&lt;br /&gt;
            if (! $resqlfix) dol_print_error($this-&amp;gt;db,&#039;Failed to update line&#039;);&lt;br /&gt;
            $obj-&amp;gt;total_tva = $tmpcal[1];&lt;br /&gt;
            $obj-&amp;gt;total_ttc = $tmpcal[2];&lt;br /&gt;
            //&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just after, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
$cf_coeff = $cf_records[$obj-&amp;gt;rowid]-&amp;gt;coefficient;&lt;br /&gt;
if (empty($cf_coeff) and $cf_coeff !== 0) $cf_coeff = 1; // by default, an empty coefficient = 1&lt;br /&gt;
$obj-&amp;gt;total_ht = $obj-&amp;gt;total_ht * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_tva = $obj-&amp;gt;total_tva * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_localtax1 = $obj-&amp;gt;total_localtax1 * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_localtax2 = $obj-&amp;gt;total_localtax2 * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_ttc = $obj-&amp;gt;total_ttc * $cf_coeff;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should do the trick. Now go to one of your invoices (or whatever module you are targetting), and try to add/edit a product/service line to force refresh the total price. You should then see this:&lt;br /&gt;
[[File:Cf_wiki_coeff_remaining_debt.png]]&lt;br /&gt;
&lt;br /&gt;
= Custom tax per product =&lt;br /&gt;
&lt;br /&gt;
This part will show you how to create a static custom tax per product. Static means that the tax will be stored per product, and not per product&#039;s line, thus you just have to setup the tax once per product and it will always be applied in every invoice you add this product.&lt;br /&gt;
&lt;br /&gt;
To do that, you will need the CustomFields Pro module + ProductsEasyAccess.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
1- Create a product&#039;s custom fields called &amp;quot;custom_tax&amp;quot;. Now, using ProductsEasyAccess, you can access this field wherever you wants, either to show it on the Dolibarr&#039;s interface (see previous chapter), or in a PDF template, which we will describe below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2- Modify your invoice&#039;s PDF template. You first need to load all the products&#039; fields &#039;&#039;before&#039;&#039; the loop over the products&#039; lines. To do this, search for a line like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// This should be placed above the &amp;quot;loop lines&amp;quot; below, add this:&lt;br /&gt;
&lt;br /&gt;
dol_include_once(&#039;/productseasyaccess/lib/productseasyaccess.lib.php&#039;);&lt;br /&gt;
fill_object_with_products_fields($object);&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines, search for this command&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  // Add this just below the for loop, this will ease your php manipulation after.&lt;br /&gt;
  // This will allow you to more easily access the products&#039; fields (this will store the id of each product inside $lineproductid).&lt;br /&gt;
  $line_product_id = $object-&amp;gt;lines[$i]-&amp;gt;fk_product;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4- You can now use your products&#039; fields in your PDF. To access the custom_tax field, just use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And to print it in your PDF:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell(0,3, $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax, 0, &#039;L&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5- Then, if you want to count the custom tax total and add it to your vat total, you can create a counter inside the for loop we cited in the first step:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_custom_tax = 0; // Initialize the custom tax total counter&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  $object-&amp;gt;total_custom_tax += $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then find inside the function **_tableau_tot**, find the following line:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And prepend the total modification like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc + $object-&amp;gt;total_custom_tax; // prepend this to add the custom tax to the total&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Done. This should show your custom tax per product and your total including all custom taxes.&lt;/div&gt;</summary>
		<author><name>Lrq3000</name></author>
	</entry>
	<entry>
		<id>https://wiki.dolibarr.org/index.php?title=File:Cf-case-cascade-contacts2.png&amp;diff=33347</id>
		<title>File:Cf-case-cascade-contacts2.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php?title=File:Cf-case-cascade-contacts2.png&amp;diff=33347"/>
		<updated>2015-04-01T14:22:14Z</updated>

		<summary type="html">&lt;p&gt;Lrq3000: Example of cascading configuration with CustomFields&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Example of cascading configuration with CustomFields&lt;/div&gt;</summary>
		<author><name>Lrq3000</name></author>
	</entry>
	<entry>
		<id>https://wiki.dolibarr.org/index.php?title=File:Cf-case-cascade-contacts1.png&amp;diff=33346</id>
		<title>File:Cf-case-cascade-contacts1.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php?title=File:Cf-case-cascade-contacts1.png&amp;diff=33346"/>
		<updated>2015-04-01T14:21:59Z</updated>

		<summary type="html">&lt;p&gt;Lrq3000: Example of cascading configuration with CustomFields&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Example of cascading configuration with CustomFields&lt;/div&gt;</summary>
		<author><name>Lrq3000</name></author>
	</entry>
	<entry>
		<id>https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33345</id>
		<title>Module CustomFields Cases</title>
		<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33345"/>
		<updated>2015-04-01T14:20:38Z</updated>

		<summary type="html">&lt;p&gt;Lrq3000: /* Cascading */ Added instructions&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:CustomFields]]&lt;br /&gt;
{{TemplateDocDevEn}}&lt;br /&gt;
{{TemplateModEN}}&lt;br /&gt;
{{BasculeDevUserEn|&lt;br /&gt;
name=CustomFields|&lt;br /&gt;
num=8500|&lt;br /&gt;
devdoc=[[Module_CustomFields_Dev]]|&lt;br /&gt;
userdoc=[[Module_CustomFields]]|}}&lt;br /&gt;
This page lists a compilation of concrete, practical examples uses of [[Module_CustomFields|CustomFields]] and of its various features.&lt;br /&gt;
&lt;br /&gt;
If you had an experience with CustomFields, you can also add here your case as an example for others to get inspired.&lt;br /&gt;
&lt;br /&gt;
= How to make a generic PDF template listing all custom fields =&lt;br /&gt;
&lt;br /&gt;
To make a generic PDF template listing all custom fields, you just need to take an existing PDF template, then you need to follow these 3 steps:&lt;br /&gt;
&lt;br /&gt;
1/ Edit the class name (required, else it will conflict with the original template).&lt;br /&gt;
&lt;br /&gt;
Eg: if we take the crabe template for invoices, we change:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;crabe&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
into:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_customfields extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;customfields&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2/ Copy this generic function at the end of the PDF:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propale/order/etc... (CustomFields simpler functions will automatically adapt)&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs); // set default PDF font size&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
    // Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
    $customfields = customfields_fill_object($object, null, $outputlangs, null, true);&lt;br /&gt;
&lt;br /&gt;
    // Setting the starting position of the text cursor&lt;br /&gt;
    $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
    $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
    // Printing the customfields&lt;br /&gt;
    foreach ($object-&amp;gt;customfields as $label=&amp;gt;$value) { // $value is already formatted!&lt;br /&gt;
        // Get translated label&lt;br /&gt;
        $translatedlabel = $customfields-&amp;gt;findLabelPDF($label, $outputlangs); // translated label of the customfield (not translated by default in customfields_fill_object() because a field should always be accessible by a base name, whatever the translation is)&lt;br /&gt;
&lt;br /&gt;
        // PDF formatting, placement and printing&lt;br /&gt;
        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
        $pdf-&amp;gt;MultiCell(0,3, $translatedlabel.&#039; ($object-&amp;gt;customfields-&amp;gt;&#039;.$label.&#039;): &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternative function (with a bit less functionalities, but more controllable):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page (used to debug if CustomFields setup is correct)&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propal/product/whatever...&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs);&lt;br /&gt;
&lt;br /&gt;
            if (empty($object-&amp;gt;table_element) or empty($object-&amp;gt;id)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;Current \$object is not compatible with CustomFields, could not find table_element or id.&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
    $customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element);&lt;br /&gt;
&lt;br /&gt;
            // Fetching custom fields records&lt;br /&gt;
            $fields = $customfields-&amp;gt;fetch($object-&amp;gt;id);&lt;br /&gt;
&lt;br /&gt;
            if (!isset($fields)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;No custom field could be found for this object. Please check your configuration (did you create at least one customfield and set a value in the current datasheet?)&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            } else {&lt;br /&gt;
                // Setting the starting position of the text cursor&lt;br /&gt;
                $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
                $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
                // Printing the customfields&lt;br /&gt;
                foreach ($fields as $key=&amp;gt;$field) {&lt;br /&gt;
                        $translatedname = $customfields-&amp;gt;findLabelPDF($key, $outputlangs); // label of the customfield&lt;br /&gt;
                        $value = $customfields-&amp;gt;simpleprintFieldPDF($key, $field, $outputlangs); // value (cleaned and properly formatted) of the customfield&lt;br /&gt;
&lt;br /&gt;
                        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
                        $pdf-&amp;gt;MultiCell(0,3, $translatedname.&#039;: &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
                        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3/ Call the _pagecustomfields function at the right place (generally, after _pagefoot and just before $pdf-&amp;gt;Close()):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Pied de page&lt;br /&gt;
$this-&amp;gt;_pagefoot($pdf,$object,$outputlangs);&lt;br /&gt;
$pdf-&amp;gt;AliasNbPages();&lt;br /&gt;
&lt;br /&gt;
// CustomFields&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) { // if the customfields module is activated...&lt;br /&gt;
        // Add a page with the customfields&lt;br /&gt;
        $pdf-&amp;gt;AddPage();&lt;br /&gt;
        $this-&amp;gt;_pagecustomfields($pdf,$object,$outputlangs);&lt;br /&gt;
        $pagenb++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
$pdf-&amp;gt;Close(); // you should first search for this, you should only find one occurrence&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then go to the module&#039;s admin panel (eg: invoice admin panel) and enable the template you&#039;ve just created.&lt;br /&gt;
&lt;br /&gt;
You can now use it to test if your customfields work.&lt;br /&gt;
&lt;br /&gt;
= Conditions on fields selection =&lt;br /&gt;
&lt;br /&gt;
Sometimes you may want to show a DropdownBox or Constraint (or another multi-choice type), but you may want to show or hide choices based on conditions (eg: only if it&#039;s a prospect, only if this zone was selected, only if this category was selected, etc..).&lt;br /&gt;
&lt;br /&gt;
To know which is the best solution you should choose to implement your condition, you should first know in which category your condition falls in.&lt;br /&gt;
&lt;br /&gt;
== Conditions categories ==&lt;br /&gt;
&lt;br /&gt;
Conditions can be categorized into 4 types:&lt;br /&gt;
&lt;br /&gt;
* static condition: choices are restrained on a condition that never changes (eg: only show third-parties that are suppliers, or lowercase all data on sql insertion, or check that a field is above a certain number, etc.).&lt;br /&gt;
* semi-dynamic condition: choices that are constrained on a static table but based on the value of another field/customfield. This is also commonly called &amp;quot;Cascaded dropdown lists&amp;quot; or &amp;quot;Dynamically linked dropdown lists&amp;quot;.&lt;br /&gt;
* dynamic: everything is dynamic: choices are based on a subfield of a field of the current module, so that there&#039;s no material table and it&#039;s impossible to create a view (because you would need to create one view per id) (eg: show only the contacts attached to the third-party being accessed)&lt;br /&gt;
* timed condition: a condition that is based on time or recurrence (eg: at 5:00 AM delete old records, etc..)&lt;br /&gt;
&lt;br /&gt;
== Conditions solutions ==&lt;br /&gt;
&lt;br /&gt;
Now that you know what kind of condition you want to set, you can choose one of the following solutions depending on your preferences and knowledge.&lt;br /&gt;
&lt;br /&gt;
In any case, you can always use the &#039;&#039;&#039;CustomFields&#039;s overload functions&#039;&#039;&#039; to fit your needs for nearly every type of condition.&lt;br /&gt;
&lt;br /&gt;
=== Static condition ===&lt;br /&gt;
&lt;br /&gt;
* Constraint WHERE clause (when creating/editing a custom field, you can specify your own WHERE conditions).&lt;br /&gt;
* View: create a view on the table based on the condition, and then create a custom field constrained on this view (which is just like any table), eg: only show third-parties that are suppliers:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
CREATE VIEW llx_societe_supplier_view AS&lt;br /&gt;
SELECT *&lt;br /&gt;
FROM llx_societe&lt;br /&gt;
WHERE fournisseur=1;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
WARNING: foreign keys (and thus constraints) can&#039;t be used on views with MySQL, and materialized views neither exist, so you can&#039;t use this method with MySQL.&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
* SQL Transformations&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
=== Semi-dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade option is ideal in most cases, and if not enough, you can use the Cascade Custom option and Overloading Functions.&lt;br /&gt;
* View&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
&lt;br /&gt;
=== Dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade Custom and Overload functions&lt;br /&gt;
* Recursive Remote Fields Access&lt;br /&gt;
* Making your own module, which calls the CustomFields class&lt;br /&gt;
&lt;br /&gt;
=== Timed condition ===&lt;br /&gt;
&lt;br /&gt;
* SQL scheduled events&lt;br /&gt;
* Cron job&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
= Cascading =&lt;br /&gt;
&lt;br /&gt;
A simple example of [http://wiki.dolibarr.org/index.php/Module_CustomFields#Cascade cascading to select a location] can be found in the user manual.&lt;br /&gt;
&lt;br /&gt;
But you can do a lot more with cascading if you combine it with other options, such as Duplication and Hide. Here is an example:&lt;br /&gt;
&lt;br /&gt;
Let&#039;s say that in invoices, you want to be able to select a contact of the society selected for this invoice. Of course, you want that the list of contacts show only the contacts related to the society (ie: that you added on the Third-Party datasheet of this society).&lt;br /&gt;
&lt;br /&gt;
To do that, you have to proceed in two steps:&lt;br /&gt;
&lt;br /&gt;
1- Create a custom field &amp;quot;soccpy_nom&amp;quot; constrained on llx_society that will duplicate the society&#039;s id. This is necessary because other custom fields can only be cascaded using other custom fields, not any Dolibarr fields. Using duplication allows you to duplicate any Dolibarr field (and even $_GET and $_POST), so that you can use them to cascade. Copy the parameters as shown in this image:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2- Create a custom field &amp;quot;contact_firstname_lastname&amp;quot; with Cascade option with parent &amp;quot;soccpy_nom&amp;quot; and constrained on the table llx_socpeople (the table of the contacts), which will show the list of contacts associated only to the selected society in soccpy_nom. Copy the parameters as shown in this image:&lt;br /&gt;
&lt;br /&gt;
And voila, you&#039;re done! The field soccpy_nom should be hidden so you won&#039;t see it, but you should see the field contact_firstname_lastname and you can check that only the contacts related to the society selected for the current invoice are shown.&lt;br /&gt;
&lt;br /&gt;
= Overloading functions =&lt;br /&gt;
&lt;br /&gt;
Here are a few concrete use cases of overloading functions to achieve various goals.&lt;br /&gt;
&lt;br /&gt;
== Dynamic list selection based on another one ==&lt;br /&gt;
&lt;br /&gt;
The goal is to show two custom fields on the Third-Party module: one which gives the zone (secteur) of the third-party (Isere, Alpes du sud, Haute-Savoie...) and the other which gives relative to the zone the list of all the ski resorts (station_a) inside the selected zone.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will show how to manually manage this kind of requirements using [[Module_CustomFields#Overloading_functions:_Adding_your_own_management_code_for_custom_fields|overloading functions]], but in latest CustomFields releases, the same solution can be achieved automatically using the [[Module_CustomFields#Cascade|Cascade option]].&lt;br /&gt;
&lt;br /&gt;
We have one table &#039;&#039;&#039;llx_k_station&#039;&#039;&#039; (rowid, station (char50), secteur(char50)) which contains the following: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1,les 2 alpes,isère&lt;br /&gt;
2,chamrousse,isère&lt;br /&gt;
3,alpe d&#039;huez,isère&lt;br /&gt;
4,vars,alpes du sud&lt;br /&gt;
5,risoul,alpes du sud&lt;br /&gt;
etc...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In CustomFields&#039;s admin panel, we create two custom fields:&lt;br /&gt;
&lt;br /&gt;
1- secteur: returns the list of all zones - type DropdownBox: enum(&#039;- Aucun&#039;,&#039;Alpes du Sud&#039;,&#039;export&#039;,&#039;Haute-Savoie&#039;,&#039;Isère&#039;, etc...)&lt;br /&gt;
&lt;br /&gt;
2- station_a: returns the list of all ski resorts - type constraint on &#039;&#039;&#039;llx_k_station&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Here is the code to put in customfields_fields_extend.lib.php that will allow to show only the ski resorts corresponding to the selected zone:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_editview_societe_station_a (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$parameters, &amp;amp;$action, &amp;amp;$user, &amp;amp;$idvar, &amp;amp;$rightok, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
  global $db; // allow to access database functions&lt;br /&gt;
  $sql=&amp;quot;SELECT llx_k_station.station, llx_k_station.rowid FROM llx_societe_customfields INNER JOIN llx_k_station ON llx_societe_customfields.Secteur = llx_k_station.secteur WHERE llx_societe_customfields.fk_societe=&amp;quot;.$object-&amp;gt;id;&lt;br /&gt;
$result=$db-&amp;gt;query($sql);&lt;br /&gt;
 &lt;br /&gt;
  if ($result) {&lt;br /&gt;
    $num = $db-&amp;gt;num_rows($result);&lt;br /&gt;
    $i = 0;&lt;br /&gt;
    $myarray = array();&lt;br /&gt;
    while ($i &amp;lt; $num)&lt;br /&gt;
    {&lt;br /&gt;
      $obj = $db-&amp;gt;fetch_object($result);&lt;br /&gt;
      $myarray []=array(&#039;id&#039;=&amp;gt;$obj-&amp;gt;rowid, &#039;value&#039;=&amp;gt;$obj-&amp;gt;station);&lt;br /&gt;
      $i++;&lt;br /&gt;
    }&lt;br /&gt;
    $value = $myarray; &lt;br /&gt;
    $db-&amp;gt;free($result);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to manub for giving this case.&lt;br /&gt;
&lt;br /&gt;
== Dynamic discount computation per products lines on propales ==&lt;br /&gt;
&lt;br /&gt;
The goal here is to compute the discount for a product line in propales based on a computation on three custom fields.&lt;br /&gt;
&lt;br /&gt;
First, create three custom fields for &amp;quot;Line commercial proposals&amp;quot;: vl, cpl and lc.&lt;br /&gt;
&lt;br /&gt;
Then add the following overloading function at the end of your customfields_fields_extend.lib.php file (read the comments for more informations):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_propaldet_vl (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
&lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $vl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;vl&#039;};&lt;br /&gt;
    $cpl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;cpl&#039;};&lt;br /&gt;
    $lc = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;lc&#039;};&lt;br /&gt;
&lt;br /&gt;
    // Compute the discount&lt;br /&gt;
    $remise_calculee = (100 - $vl) * (100 - $cpl) * (100 - $lc);&lt;br /&gt;
&lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $remise_calculee = price2num($remise_calculee);&lt;br /&gt;
&lt;br /&gt;
    // Assign the new discount into the PropalLine object&lt;br /&gt;
    $object-&amp;gt;remise_percent = $remise_calculee;&lt;br /&gt;
&lt;br /&gt;
    // Update the price (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
&lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
&lt;br /&gt;
    //$q = &amp;quot;UPDATE  &amp;quot;.MAIN_DB_PREFIX.&amp;quot;propaldet SET remise_percent=&amp;quot;.$remise_calculee.&amp;quot; WHERE  rowid=&amp;quot;.$object-&amp;gt;rowid;&lt;br /&gt;
    //$db-&amp;gt;query($q);&lt;br /&gt;
    &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;vl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;cpl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;lc&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Linking Dolibarr objects from two different modules =&lt;br /&gt;
&lt;br /&gt;
Dolibarr sometimes offers the possibility to link two different modules. Eg: when you convert an Intervention card into an Invoice.&lt;br /&gt;
&lt;br /&gt;
In this case, you might want in your templates to fetch the custom fields of the linked object. Eg: you generate a PDF from the Invoice, but you want to get the custom fields from the Intervention that was converted into the Invoice.&lt;br /&gt;
&lt;br /&gt;
The key here is to use the &#039;&#039;&#039;llx_element_element&#039;&#039;&#039; table or the &#039;&#039;&#039;llx_element_*&#039;&#039;&#039; tables (eg: llx_element_contact): this is a standard Dolibarr table that stores all links between objects of two different modules.&lt;br /&gt;
&lt;br /&gt;
Here is the structure:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
rowid | fk_source | sourcetype | fk_target | targettype&lt;br /&gt;
-------------------------------------------------------&lt;br /&gt;
1 | 2 | fichinter | 5 | facture&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can then use a php code like the following to populate your $object with customfields:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
// This will also create and returns us an instance of the CustomFields class, which we will use to manually query the llx_element_element table&lt;br /&gt;
$customfields = customfields_fill_object($object, null, $outputlangs, &#039;facture&#039;, true); // beautified values&lt;br /&gt;
&lt;br /&gt;
// Querying the llx_element_element table manually to get the link between the invoice and the linked intervention card (we want the id of the intervention card)&lt;br /&gt;
// fetchAny($columns, $table, $where=&#039;&#039;, $orderby=&#039;&#039;, $limitby=&#039;&#039;)&lt;br /&gt;
$eltrow = $customfields-&amp;gt;fetchAny(&#039;*&#039;, MAIN_DB_PREFIX.&#039;element_element&#039;, &#039;targettype=&amp;quot;&#039;.$object-&amp;gt;element.&#039;&amp;quot; and fk_target=&#039;.$object-&amp;gt;id.&#039; and sourcetype=&amp;quot;fichinter&amp;quot;&#039;);&lt;br /&gt;
&lt;br /&gt;
// Save the intervention&#039;s id&lt;br /&gt;
$sourceid = $eltrow[0][&#039;fk_source&#039;];&lt;br /&gt;
&lt;br /&gt;
// Crafting a dummy intervention object&lt;br /&gt;
$fromobject = new stdClass(); // Init an empty object&lt;br /&gt;
$fromobject-&amp;gt;id = $sourceid; // We need the id and..&lt;br /&gt;
$fromobject-&amp;gt;table_element = &#039;fichinter&#039;; // the table_element (name of the table)&lt;br /&gt;
&lt;br /&gt;
// We can then fill $object with the intervention custom fields (we store them in a sub property &#039;fichinter&#039; to avoid conflicts with invoice&#039;s custom fields)&lt;br /&gt;
customfields_fill_object($object, $fromobject, $outputlangs, &#039;fichinter&#039;, true);&lt;br /&gt;
&lt;br /&gt;
// Now you can access invoices custom fields using:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;facture-&amp;gt;cf_myfield);&lt;br /&gt;
// And access intervention custom fields with:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;fichinter-&amp;gt;cf_myfield);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to netassopro for the tip.&lt;br /&gt;
&lt;br /&gt;
= Modify the total global price with a coefficient (without tampering the discount) =&lt;br /&gt;
&lt;br /&gt;
This is more troublesome than modifying the total price per product/service line, because the total global price of an object (let&#039;s say an invoice, but this also works for any other object) is always recomputed by summing the prices and quantity of each product/service line. This means that although there&#039;s an entry &amp;quot;total_ttc&amp;quot; in the database to store the invoice&#039;s total price, this price is in fact always recomputed, it&#039;s never used as-is. Thus, it&#039;s useless to change only the database record: we need to inject some code to change how the total price is computed.&lt;br /&gt;
&lt;br /&gt;
This could be done using an overloading function or a trigger, but unluckily, Dolibarr doesn&#039;t call a trigger nor a hook when recomputing the price. We thus need to edit core files to do what we want.&lt;br /&gt;
&lt;br /&gt;
However, the bright side is that we can cleverly edit just two functions from one core file: update_price() and getMarginInfos() from /htdocs/core/class/commonobject.class.php , and everything should run smoothly. Beware that if you update Dolibarr, you will need to redo these two modifications in the code.&lt;br /&gt;
&lt;br /&gt;
First, you need to create a new custom field called &amp;quot;global_coefficient&amp;quot; in Invoices (not Invoices Lines!). Then you can continue to the subchapters below.&lt;br /&gt;
&lt;br /&gt;
Also note that this global coefficient is also compatible with per line coefficients (as described in the next chapter), thus you can use a global coefficient and per line coefficients at the same time. You can also use multiple global coefficient if you wish.&lt;br /&gt;
&lt;br /&gt;
Another note: if you have a grid of discounts depending on quantity or price or whatever parameter, you can program a precomputation code using a Custom Ajax function (this way, your discounts will be automatically computed, but then you can still manually enter them for particular cases since the custom field is editable).&lt;br /&gt;
&lt;br /&gt;
== Implement the change of global price in most of Dolibarr (including remaining debt to pay and PDF and ODT) ==&lt;br /&gt;
&lt;br /&gt;
The change we will implement here will reflect the new total price with the global coefficient in most of Dolibarr&#039;s code, since we will here change the main function that is usually called to update and get the total price.&lt;br /&gt;
&lt;br /&gt;
Open /htdocs/core/class/commonobject.class.php and look for the function update_price().&lt;br /&gt;
&lt;br /&gt;
You should see the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function update_price($exclspec=0,$roundingadjust=&#039;none&#039;,$nodatabaseupdate=0,$seller=&#039;&#039;)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just below the include_once line, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;;&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/conf/conf_customfields_func.lib.php&#039;;&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $this-&amp;gt;table_element);&lt;br /&gt;
// Get object&#039;s id (either stored in property &amp;quot;id&amp;quot; or &amp;quot;rowid&amp;quot;)&lt;br /&gt;
$this_id = (!empty($this-&amp;gt;id)) ? $this-&amp;gt;id : $this-&amp;gt;rowid;&lt;br /&gt;
// Load custom fields records&lt;br /&gt;
$cf_parent_record = $customfields-&amp;gt;fetch($this_id);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will load the global_coefficient custom field (and also any other custom field you have defined for invoices).&lt;br /&gt;
&lt;br /&gt;
Now scroll below, and you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
while ($i &amp;lt; $num)&lt;br /&gt;
{&lt;br /&gt;
    $obj = $this-&amp;gt;db-&amp;gt;fetch_object($resql);&lt;br /&gt;
&lt;br /&gt;
    ... (lots of code)&lt;br /&gt;
&lt;br /&gt;
    i++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Add revenue stamp to total&lt;br /&gt;
$this-&amp;gt;total_ttc       += isset($this-&amp;gt;revenuestamp)?$this-&amp;gt;revenuestamp:0;&lt;br /&gt;
&lt;br /&gt;
$this-&amp;gt;db-&amp;gt;free($resql);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is the end of the loop that is summing each product/service line price to compute the total price. Just after this loop, you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Now update global field total_ht, total_ttc and tva&lt;br /&gt;
$fieldht=&#039;total_ht&#039;;&lt;br /&gt;
$fieldtva=&#039;tva&#039;;&lt;br /&gt;
$fieldlocaltax1=&#039;localtax1&#039;;&lt;br /&gt;
$fieldlocaltax2=&#039;localtax2&#039;;&lt;br /&gt;
$fieldttc=&#039;total_ttc&#039;;&lt;br /&gt;
// Specific code for backward compatibility with old field names&lt;br /&gt;
if ($this-&amp;gt;element == &#039;facture&#039; || $this-&amp;gt;element == &#039;facturerec&#039;)             $fieldht=&#039;total&#039;;&lt;br /&gt;
if ($this-&amp;gt;element == &#039;facture_fourn&#039; || $this-&amp;gt;element == &#039;invoice_supplier&#039;) $fieldtva=&#039;total_tva&#039;;&lt;br /&gt;
if ($this-&amp;gt;element == &#039;propal&#039;)                                                $fieldttc=&#039;total&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Between those two blocks of code (so just above // Now update global field total_ht, total_ttc and tva), you can place the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
if (!empty($cf_parent_record-&amp;gt;global_coefficient)) { // avoid applying the coefficient if empty (this will return a 0 price)&lt;br /&gt;
    $this-&amp;gt;total_ht = $this-&amp;gt;total_ht * $cf_parent_record-&amp;gt;global_coefficient;&lt;br /&gt;
    $this-&amp;gt;total_tva = $this-&amp;gt;total_tva * $cf_parent_record-&amp;gt;global_coefficient;&lt;br /&gt;
    $this-&amp;gt;total_ttc = $this-&amp;gt;total_ttc * $cf_parent_record-&amp;gt;global_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Done, now try to add/edit a product/service line, and the total price should be affected by the global coefficient.&lt;br /&gt;
&lt;br /&gt;
Note that this modification &#039;&#039;&#039;needs only to be done once for all modules&#039;&#039;&#039;, you then just have to create a custom field named &amp;quot;global_coefficient&amp;quot; in every module you want to apply this coefficient.&lt;br /&gt;
&lt;br /&gt;
== Implementing the change of price in margin module ==&lt;br /&gt;
&lt;br /&gt;
If you enabled the margin module, you should see that the coefficient is not applied there, but it&#039;s easy to fix that.&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/class/commonobject.class.php and find the function getMarginInfos(), then inside find the following lines:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach($this-&amp;gt;lines as $line) {&lt;br /&gt;
    if (empty($line-&amp;gt;pa_ht) &amp;amp;&amp;amp; isset($line-&amp;gt;fk_fournprice) &amp;amp;&amp;amp; !$force_price) {&lt;br /&gt;
        $product = new ProductFournisseur($this-&amp;gt;db);&lt;br /&gt;
        if ($product-&amp;gt;fetch_product_fournisseur_price($line-&amp;gt;fk_fournprice))&lt;br /&gt;
            $line-&amp;gt;pa_ht = $product-&amp;gt;fourn_unitprice * (1 - $product-&amp;gt;fourn_remise_percent / 100);&lt;br /&gt;
        if (isset($conf-&amp;gt;global-&amp;gt;MARGIN_TYPE) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;MARGIN_TYPE == &amp;quot;2&amp;quot; &amp;amp;&amp;amp; $product-&amp;gt;fourn_unitcharges &amp;gt; 0)&lt;br /&gt;
            $line-&amp;gt;pa_ht += $product-&amp;gt;fourn_unitcharges;&lt;br /&gt;
    }&lt;br /&gt;
    // si prix d&#039;achat non renseigné et devrait l&#039;être, alors prix achat = prix vente&lt;br /&gt;
    if ((!isset($line-&amp;gt;pa_ht) || $line-&amp;gt;pa_ht == 0) &amp;amp;&amp;amp; $line-&amp;gt;subprice &amp;gt; 0 &amp;amp;&amp;amp; (isset($conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull == 1)) {&lt;br /&gt;
        $line-&amp;gt;pa_ht = $line-&amp;gt;subprice * (1 - ($line-&amp;gt;remise_percent / 100));&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the lines are filled with margin infos. The price has just been fetched here, and we will now modify it with the coefficient before the price gets used for margin computation.&lt;br /&gt;
&lt;br /&gt;
To optimize things up, we will load the custom fields before the loop (because they don&#039;t change between product/service lines, they are the custom fields of the parent object), and then apply the global_coefficient inside the loop.&lt;br /&gt;
&lt;br /&gt;
Just above the code we found (just before the for loop), paste the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
// Get object&#039;s id (either stored in property &amp;quot;id&amp;quot; or &amp;quot;rowid&amp;quot;)&lt;br /&gt;
$this_id = (!empty($this-&amp;gt;id)) ? $this-&amp;gt;id : $this-&amp;gt;rowid;&lt;br /&gt;
// Load custom fields records&lt;br /&gt;
$customfields = customfields_fill_object($this);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And now, just below the code we found, inside the loop (and just above the comment: &amp;quot;// calcul des marges&amp;quot;), paste the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
// Apply the coefficient&lt;br /&gt;
if (!empty($this-&amp;gt;customfields-&amp;gt;cf_global_coefficient)) {&lt;br /&gt;
    $line-&amp;gt;pa_ht = $line-&amp;gt;pa_ht * $this-&amp;gt;customfields-&amp;gt;cf_global_coefficient;&lt;br /&gt;
    $line-&amp;gt;subprice = $line-&amp;gt;subprice * $this-&amp;gt;customfields-&amp;gt;cf_global_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that here we cheated a bit: we do not compute the coefficient on the global total price, but rather per product/service line price. This is done on purpose, because mathematically this should produce no difference, and it also allows Dolibarr to continue peacefully with its margin calculations (else we would have to adapt all those computations, which would imply a lot more of code changes!).&lt;br /&gt;
&lt;br /&gt;
That&#039;s all, the price should be reflected in the margin. Just refresh the page to reflect the change.&lt;br /&gt;
&lt;br /&gt;
Note that this modification &#039;&#039;&#039;needs only to be done once for all modules&#039;&#039;&#039;, you then just have to create a custom field named &amp;quot;global_coefficient&amp;quot; in every module you want to apply this coefficient.&lt;br /&gt;
&lt;br /&gt;
== Refresh the price instantly ==&lt;br /&gt;
&lt;br /&gt;
Maybe you noticed one glitch: when you modify the global_coefficient, the global total price isn&#039;t updated until you add/edit a product/service line. This is because the update_price() function is only called when we add/edit a product/service line.&lt;br /&gt;
&lt;br /&gt;
To fix that, we need now to force the refreshing of price. To do that, we will use an &amp;quot;aftersave&amp;quot; overloading function, which will execute commands after our custom field is saved.&lt;br /&gt;
&lt;br /&gt;
Go to the folder /htdocs/customfields/fields and rename the file customfields_fields_extend.lib.php.example into customfields_fields_extend.lib.php .&lt;br /&gt;
&lt;br /&gt;
Now, open it in your favourite text editor, and add the following function :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_aftersave_facture_global_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value, $fields) {&lt;br /&gt;
    // Force refreshing total price&lt;br /&gt;
    $object-&amp;gt;update_price(0, &#039;auto&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s all. Now, when you edit your global_coefficient, this will force refresh the total price.&lt;br /&gt;
&lt;br /&gt;
However, you will still notice one last glitch: when you edit the global_coefficient, the total price isn&#039;t reflected instantly, you must refresh the webpage once to see the new price. This is because of how the datasheet and the CustomFields hook are designed: CustomFields is called only exactly where the custom fields are place, thus it is called after the total price, the margin and the remaining debt are printed. Thus, CustomFields updates the value of your custom field only after all those informations get printed, and thus it cannot modify them at this stage. That&#039;s why those information are out-of-date, and you must refresh the webpage once to get to see the latest values.&lt;br /&gt;
&lt;br /&gt;
There is a workaround for this: refresh automatically the webpage (but delete the POST data to avoid an infinite loop). You just need to use this overloading code instead of the one above:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_aftersave_facture_global_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value, $fields) {&lt;br /&gt;
    // Force refreshing total price&lt;br /&gt;
    $object-&amp;gt;update_price(0, &#039;auto&#039;);&lt;br /&gt;
    // Force refresh the page to update prices that were printed before the custom field aftersave was triggered (and only if not creating the object because then there&#039;s no need to refresh)&lt;br /&gt;
    if ($action !== &#039;add&#039;) print(&#039;&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;window.location.href = window.location.pathname + window.location.search;&amp;lt;/script&amp;gt;&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Another way to fix this issue would be to modify how CustomFields save the custom fields, by moving this stage at the very top. However, this would need another hook, which should be called at the very beginning of the page loading, which should be made only for this purpose (PageLoading), and should be available in all modules supported by CustomFields in order to not break anything.&lt;br /&gt;
&lt;br /&gt;
Note that you will need to create one overloading function for each module you want to support with the global_coefficient custom field. For example, if you want to support both invoices and commercial proposals, you will need two overloading functions:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient for invoices (note the &amp;quot;facture&amp;quot; in the name of the function)&lt;br /&gt;
function customfields_field_aftersave_facture_global_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value, $fields) {&lt;br /&gt;
    // Force refreshing total price&lt;br /&gt;
    $object-&amp;gt;update_price(0, &#039;auto&#039;);&lt;br /&gt;
    // Force refresh the page to update prices that were printed before the custom field aftersave was triggered (and only if not creating the object because then there&#039;s no need to refresh)&lt;br /&gt;
    if ($action !== &#039;add&#039;) print(&#039;&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;window.location.href = window.location.pathname + window.location.search;&amp;lt;/script&amp;gt;&#039;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Global coefficient for commercial proposals (note the &amp;quot;propal&amp;quot; in the name of the function)&lt;br /&gt;
function customfields_field_aftersave_propal_global_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value, $fields) {&lt;br /&gt;
    // Force refreshing total price&lt;br /&gt;
    $object-&amp;gt;update_price(0, &#039;auto&#039;);&lt;br /&gt;
    // Force refresh the page to update prices that were printed before the custom field aftersave was triggered (and only if not creating the object because then there&#039;s no need to refresh)&lt;br /&gt;
    if ($action !== &#039;add&#039;) print(&#039;&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;window.location.href = window.location.pathname + window.location.search;&amp;lt;/script&amp;gt;&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Modify the total price PER product/service line with a coefficient (without tampering the discount) =&lt;br /&gt;
&lt;br /&gt;
Here we will go through the creation and management of a custom field called &#039;&#039;&#039;coefficient&#039;&#039;&#039; that will change the total price of each product by modifying it by the coefficient.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will not create a coefficient per product but rather per product&#039;s line. Thus if you want to create a coefficient that never change between invoices but only depending on the product and stays static, please refer to the next chapter below about creating a custom tax per product.&lt;br /&gt;
&lt;br /&gt;
The preliminary and necessary step is to first create a custom field that will be used to modify the total price. We will create one for Client Invoice Lines.&lt;br /&gt;
&lt;br /&gt;
Go to the CustomFields administration panel, then into the Client Invoice Lines tab, then click on the button New Field, name the field &#039;&#039;&#039;coefficient&#039;&#039;&#039;, and set the type to &#039;&#039;&#039;Double&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
When you go to client invoices in Dolibarr, you should then see something similar to this:&lt;br /&gt;
[[File:Cfe1.jpg]]&lt;br /&gt;
You can see that our coefficient field was successfully created.&lt;br /&gt;
&lt;br /&gt;
Then here are two alternative ways of implementing the change of the total price of each product relativery to this coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change total price in database ==&lt;br /&gt;
&lt;br /&gt;
Here we will use a &amp;quot;save&amp;quot; overloading function to change the total price directly in the database, thus we won&#039;t have to modify anything else (eg: nor PDF templating nor ODT) since the total price will be already updated in the database.&lt;br /&gt;
&lt;br /&gt;
Warning: this method will work only with CustomFields =&amp;gt; 3.2.23 since the &amp;quot;save&amp;quot; overloading function did not work for products lines before.&lt;br /&gt;
&lt;br /&gt;
Just add the following overloading function in your customfields_fields_extend.lib.php file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_facturedet_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
 &lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $coefficient = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;coefficient&#039;}; // you could also use $coefficient = $value;&lt;br /&gt;
 &lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $coefficient = price2num($coefficient);&lt;br /&gt;
 &lt;br /&gt;
    // Get the original subprices (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
 &lt;br /&gt;
    // Update the subprices with the coefficient&lt;br /&gt;
    $object-&amp;gt;total_ht = $object-&amp;gt;total_ht * $coefficient; // compute no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva = $object-&amp;gt;total_tva * $coefficient; // compute tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc * $coefficient; // compute tax included price&lt;br /&gt;
 &lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(&#039;&#039;,true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
 &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;coefficient&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should show the correct total price on the Dolibarr&#039;s interface without modifying the unit subprice (P.U. Unit):&lt;br /&gt;
&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
You have nothing to edit in your PDF or ODT template, since the new subprice with applied coefficient will be automatically printed instead of the old one without coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change printed total price per product without tampering the subprice in the database ==&lt;br /&gt;
&lt;br /&gt;
Here is a way to change the printed total price (the one shown on screen and in PDF/ODT) but without changing the total price in the database.&lt;br /&gt;
&lt;br /&gt;
The following sub-chapters are all independent, thus you can only follow the ones that you require, eg: if you only need to change the price in your PDF documents, just create a custom field then jump to the sub-chapter to &#039;&#039;&#039;Implement the change of price in PDF&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in ODT templates ===&lt;br /&gt;
&lt;br /&gt;
There&#039;s no eval implementation currently inside ODT templates, but there is a kind of hook implementation, called [[Create_an_ODT_document_template#Other_personalized_tags|substitution functions]].&lt;br /&gt;
&lt;br /&gt;
You can either create your own module, then create your own substitution function, but for the sake of simplicity in this tutorial, we will directly use the CustomFields substitution function. However, please remember that you will have to redo your changes everytime you will update the CustomFields module, so if you don&#039;t want to do that it would be better if you create your own module and substitution function.&lt;br /&gt;
&lt;br /&gt;
Open the file &#039;&#039;&#039;htdocs/customfields/core/substitutions/functions_customfields.lib.php&#039;&#039;&#039; and go to the function &#039;&#039;&#039;customfields_completesubstitutionarray_lines&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
At the very end of this function, but inside the customfields check:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) {&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
But just before the ending bracket, you should add the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($object-&amp;gt;table_element_line === &#039;facturedet&#039;) { // First check that we are in the right module, we don&#039;t want to use coefficient for other modules if we didn&#039;t define the custom field there&lt;br /&gt;
    $substitutionarray[&#039;line_price_full&#039;] = $substitutionarray[&#039;line_price_ttc&#039;] * $substitutionarray[&#039;cf_coefficient&#039;]; // multiply the total price with our coefficient&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code will add a new tag &#039;&#039;&#039;{line_price_full}&#039;&#039;&#039; that you can then use inside your ODT templates.&lt;br /&gt;
&lt;br /&gt;
Just place this tag in a table in your ODT document, between the table tags ( [!-- BEGIN row.lines --] ... [!-- END row.lines --] ), and this will print your full price.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in PDF templates ===&lt;br /&gt;
&lt;br /&gt;
First, we will create our own PDF template by copying an existing template, and then we will edit it to change the price of items.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Creating your own PDF template by copying an existing template&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Go into the following folder: &#039;&#039;&#039;htdocs/core/modules/facture/doc/&#039;&#039;&#039;, then copy the file &#039;&#039;&#039;pdf_crabe.modules.php&#039;&#039;&#039; and name the new one &#039;&#039;&#039;pdf_crabecoeff.modules.php&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Then open it in your favourite editor, and change the following lines:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures // change pdf_crabe into pdf_crabecoeff&lt;br /&gt;
{&lt;br /&gt;
...&lt;br /&gt;
    function __construct($db)&lt;br /&gt;
    {&lt;br /&gt;
        ...&lt;br /&gt;
	$this-&amp;gt;name = &amp;quot;crabe&amp;quot;; // change &amp;quot;crabe&amp;quot; into &amp;quot;crabecoeff&amp;quot;&lt;br /&gt;
        ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you have your own PDF template!&lt;br /&gt;
&lt;br /&gt;
Now you should &#039;&#039;&#039;enable your PDF template&#039;&#039;&#039; before doing anything else, by going into the Modules Administration panel, and into the Invoice admin page, then click on the ON button at the right of the &#039;&#039;&#039;crabecoeff&#039;&#039;&#039; entry to enable the template.&lt;br /&gt;
&lt;br /&gt;
Now, let&#039;s proceed onto modifying the prices.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Changing the prices automatically in your PDF&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
You can now do pretty much anything you want in your PDF template, but I will give you a mean to automatically change the prices of every products with our coefficient, as well as the total price, without editing too much the template.&lt;br /&gt;
&lt;br /&gt;
First, try to find the following code of line (should be inside the write_file() method):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf=pdf_getInstance($this-&amp;gt;format);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, just copy/paste the following code &#039;&#039;&#039;below&#039;&#039;&#039; pdf_getInstance():&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$this-&amp;gt;customfields = customfields_fill_object($object, null, $outputlangs, null, true); // beautified values&lt;br /&gt;
$this-&amp;gt;customfields_raw = customfields_fill_object($object, null, $outputlangs, &#039;raw&#039;, null); // raw values&lt;br /&gt;
$this-&amp;gt;customfields_lines = customfields_fill_object_lines($object, null, $outputlangs, null, true); // product lines&#039; values&lt;br /&gt;
&lt;br /&gt;
// Resetting every $object price because we will compute them anew&lt;br /&gt;
$object-&amp;gt;total_ht = 0;&lt;br /&gt;
$object-&amp;gt;total_tva = 0;&lt;br /&gt;
$object-&amp;gt;total_ttc = 0;&lt;br /&gt;
// For each line of product/service, we will recompute all the prices&lt;br /&gt;
foreach($object-&amp;gt;lines as $line) {&lt;br /&gt;
    // Get the coefficient custom field&#039;s value for the current line&lt;br /&gt;
    $coeff = $object-&amp;gt;customfields-&amp;gt;lines-&amp;gt;{$line-&amp;gt;rowid}-&amp;gt;cf_coefficient;&lt;br /&gt;
    // Recompute the price of this product/service line&lt;br /&gt;
    $line-&amp;gt;total_ht = $line-&amp;gt;total_ht*$coeff; // compute no tax price&lt;br /&gt;
    $line-&amp;gt;total_tva = $line-&amp;gt;total_tva*$coeff; // compute tax amount&lt;br /&gt;
    $line-&amp;gt;total_ttc = $line-&amp;gt;total_ttc*$coeff; // compute tax included price&lt;br /&gt;
    // Do the sum of all new products/services prices and tva (will be incremented with every product/service line)&lt;br /&gt;
    $object-&amp;gt;total_ht += $line-&amp;gt;total_ht; // global no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva += $line-&amp;gt;total_tva; // global vat/tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc += $line-&amp;gt;total_ttc; // global  tax included price&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you should try to generate your PDF document, and you should get something like this:&lt;br /&gt;
[[File:Cfe3.jpg]]&lt;br /&gt;
&lt;br /&gt;
Note: printing the coefficient as a column in your PDF document is also possible but will not be covered here because PDF templates will vary for every persons depending on your needs.&lt;br /&gt;
For guidelines about how to do this, you can read the documentation on [[Create_a_PDF_document_template|how to create a PDF template]] and [[Module_CustomFields#Implementing_in_PDF_templates|how to use custom fields in PDF templates]].&lt;br /&gt;
&lt;br /&gt;
Hint: you should be looking inside the following conditional block, inside the write_file() method:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
{&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of total price in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
Unluckily there is not (yet) any hooking context to modify how lines of products/services are printed, thus the only thing you can do is directly edit the &#039;&#039;&#039;template file&#039;&#039;&#039; and manually put inside your code to load your custom field and print it the way you want using HTML formatting, for our example:&lt;br /&gt;
&lt;br /&gt;
* For Dolibarr 3.3 and above, edit the file &#039;&#039;&#039;htdocs/core/tpl/objectline_view.tpl.php&#039;&#039;&#039;&lt;br /&gt;
* For Dolibarr 3.1 and 3.2, edit the file &#039;&#039;&#039;htdocs/core/tpl/freeproductline_view.tpl.php&#039;&#039;&#039; AND &#039;&#039;&#039;predefinedproductline_view.tpl.php&#039;&#039;&#039; (apply the same changes to both files)&lt;br /&gt;
&lt;br /&gt;
In the template file you opened, find the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And replace it by:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $object-&amp;gt;table_element_line; // you can use $object-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
&lt;br /&gt;
// Modifying the HT price and printing it on the Dolibarr&#039;s datasheet interface&lt;br /&gt;
// Applying a multiplying/dividing coefficient to an HT price will give a mathematically correct TTC price since the taxes are also a multiplier&lt;br /&gt;
// However, if you want to apply other mathematical operations (like addition or substaction), you should use the total price with $line-&amp;gt;total_ttc&lt;br /&gt;
if (!empty($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
    echo price($line-&amp;gt;total_ht * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient);&lt;br /&gt;
} else {&lt;br /&gt;
    echo price($line-&amp;gt;total_ht);&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should produce something like this:&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Technical note: here we are using customfields_fill_object() instead of customfields_fill_object_lines() (the latter being specifically adapted to lines and thus should theoretically be used here) for performance reasons because the template file we are editing is called in a loop, and customfields_fill_object_lines() should be called outside a loop (because it already does the job that is done by the loop), so here we &#039;tricks&#039; the customfields_fill_object() function to avoid this loop by submitting &#039;facturedet&#039; as a module by itself (when in fact it&#039;s a product lines table, but it works the same way nevertheless). This is a good demonstration of the flexibility of the CustomFields functions and methods.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the coefficient column in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
It&#039;s also possible to show the coefficient in a new column on the Dolibarr interface.&lt;br /&gt;
&lt;br /&gt;
To do that, you need to do two things:&lt;br /&gt;
&lt;br /&gt;
1- Add the column title &amp;quot;Coeff.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
2- Add the generator that will input the coefficient value for each line.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Add the column title &amp;quot;Coeff.&amp;quot;&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
Open in your favourite editor the file /htdocs/core/class/commonobject.class.php and inside the function printObjectLines (note the plural s), find the following lines of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Total HT&lt;br /&gt;
print &#039;&amp;lt;td align=&amp;quot;right&amp;quot; width=&amp;quot;50&amp;quot;&amp;gt;&#039;.$langs-&amp;gt;trans(&#039;TotalHTShort&#039;).&#039;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is what prints the Total HT price column. We will place the Coeff column just before.&lt;br /&gt;
&lt;br /&gt;
So, just above this code, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
dol_include_once(&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element_line);&lt;br /&gt;
$coeff_field = $customfields-&amp;gt;fetchFieldStruct(&#039;coefficient&#039;);&lt;br /&gt;
if (!empty($coeff_field)) {&lt;br /&gt;
    print &#039;&amp;lt;td align=&amp;quot;right&amp;quot; width=&amp;quot;50&amp;quot;&amp;gt;&#039;.$langs-&amp;gt;trans(&#039;Coeff.&#039;).&#039;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will take care of showing the Coeff. column, but only for modules where you created such a custom field (because the file we modified is a common object for all modules in Dolibarr, not just invoices, thus this modification will gracefully skip this custom field if it does not exists for other modules).&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Add the generator&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/tpl/objectline_view.tpl.php and try to find the following block of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php if ($line-&amp;gt;special_code == 3)	{ ?&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo $langs-&amp;gt;trans(&#039;Option&#039;); ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } else { ?&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is what prints the total HT price value for each line.&lt;br /&gt;
&lt;br /&gt;
Just above of this block of code, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $object-&amp;gt;table_element_line; // you can use $this-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
if (isset($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
    &amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient; ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just like for the column title, this code will take care of generating the coefficient value only if the custom field is defined, because this file is also a generic template for all modules of Dolibarr.&lt;br /&gt;
&lt;br /&gt;
At this point, everything is done, you should see the following:&lt;br /&gt;
[[File:Cf_wiki_coeff_column.png]]&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in margin module ===&lt;br /&gt;
&lt;br /&gt;
If you enabled the margin module, you should see that the coefficient is not applied there, but it&#039;s easy to fix that.&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/class/commonobject.class.php and find the function getMarginInfos(), then inside find the following lines:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach($this-&amp;gt;lines as $line) {&lt;br /&gt;
    if (empty($line-&amp;gt;pa_ht) &amp;amp;&amp;amp; isset($line-&amp;gt;fk_fournprice) &amp;amp;&amp;amp; !$force_price) {&lt;br /&gt;
        $product = new ProductFournisseur($this-&amp;gt;db);&lt;br /&gt;
        if ($product-&amp;gt;fetch_product_fournisseur_price($line-&amp;gt;fk_fournprice))&lt;br /&gt;
            $line-&amp;gt;pa_ht = $product-&amp;gt;fourn_unitprice * (1 - $product-&amp;gt;fourn_remise_percent / 100);&lt;br /&gt;
        if (isset($conf-&amp;gt;global-&amp;gt;MARGIN_TYPE) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;MARGIN_TYPE == &amp;quot;2&amp;quot; &amp;amp;&amp;amp; $product-&amp;gt;fourn_unitcharges &amp;gt; 0)&lt;br /&gt;
            $line-&amp;gt;pa_ht += $product-&amp;gt;fourn_unitcharges;&lt;br /&gt;
    }&lt;br /&gt;
    // si prix d&#039;achat non renseigné et devrait l&#039;être, alors prix achat = prix vente&lt;br /&gt;
    if ((!isset($line-&amp;gt;pa_ht) || $line-&amp;gt;pa_ht == 0) &amp;amp;&amp;amp; $line-&amp;gt;subprice &amp;gt; 0 &amp;amp;&amp;amp; (isset($conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull == 1)) {&lt;br /&gt;
        $line-&amp;gt;pa_ht = $line-&amp;gt;subprice * (1 - ($line-&amp;gt;remise_percent / 100));&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the lines are filled with margin infos. The price has just been fetched here, and we will now modify it with the coefficient before the price gets used for margin computation.&lt;br /&gt;
&lt;br /&gt;
Just below the code we found, paste the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $line-&amp;gt;table_element;&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
if (!empty($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
    $line-&amp;gt;pa_ht = $line-&amp;gt;pa_ht * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient;&lt;br /&gt;
    $line-&amp;gt;subprice = $line-&amp;gt;subprice * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s all, the price should be reflected in the margin.&lt;br /&gt;
[[File:Cf_wiki_coeff_margin.png]]&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in remaining debt to pay ===&lt;br /&gt;
&lt;br /&gt;
Open /htdocs/core/class/commonobject.class.php and look for the function update_price().&lt;br /&gt;
&lt;br /&gt;
You should see the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function update_price($exclspec=0,$roundingadjust=&#039;none&#039;,$nodatabaseupdate=0,$seller=&#039;&#039;)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just below the include_once line, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;;&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/conf/conf_customfields_func.lib.php&#039;;&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $this-&amp;gt;table_element_line);&lt;br /&gt;
// Fetching line ids from parent object id&lt;br /&gt;
$line_ids = $customfields-&amp;gt;fetchAny(&#039;rowid&#039;, MAIN_DB_PREFIX.$this-&amp;gt;table_element_line, $this-&amp;gt;fk_element.&#039; = &#039;.$this-&amp;gt;id);&lt;br /&gt;
$line_ids = array_values_recursive(&#039;rowid&#039;, $line_ids);&lt;br /&gt;
// Load custom fields&lt;br /&gt;
$cf_records = $customfields-&amp;gt;fetch($line_ids);&lt;br /&gt;
// Reassociate the records with the line id (instead of the customfields record id)&lt;br /&gt;
$cf_line_cfrowid_to_rowid = array_reassociate(&#039;rowid&#039;, &#039;fk_&#039;.$this-&amp;gt;table_element_line, $cf_records);&lt;br /&gt;
$tmp = $cf_records;&lt;br /&gt;
$cf_records = array();&lt;br /&gt;
foreach ($tmp as $k=&amp;gt;$v) { $cf_records[$cf_line_cfrowid_to_rowid[$k]] = $tmp[$k]; }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will load the coefficient custom field (and also any other custom field you have defined for lines).&lt;br /&gt;
&lt;br /&gt;
Now scroll below, and you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
while ($i &amp;lt; $num)&lt;br /&gt;
{&lt;br /&gt;
    $obj = $this-&amp;gt;db-&amp;gt;fetch_object($resql);&lt;br /&gt;
&lt;br /&gt;
    // Note: There is no check on detail line and no check on total, if $forcedroundingmode = &#039;none&#039;&lt;br /&gt;
&lt;br /&gt;
    if ($forcedroundingmode == &#039;0&#039;)	// Check if data on line are consistent. This may solve lines that were not consistent because set with $forcedroundingmode=&#039;auto&#039;&lt;br /&gt;
    {&lt;br /&gt;
        $localtax_array=array($obj-&amp;gt;localtax1_type,$obj-&amp;gt;localtax1_tx,$obj-&amp;gt;localtax2_type,$obj-&amp;gt;localtax2_tx);&lt;br /&gt;
        $tmpcal=calcul_price_total($obj-&amp;gt;qty, $obj-&amp;gt;up, $obj-&amp;gt;remise_percent, $obj-&amp;gt;vatrate, $obj-&amp;gt;localtax1_tx, $obj-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $obj-&amp;gt;info_bits, $obj-&amp;gt;product_type, $seller, $localtax_array);&lt;br /&gt;
        $diff=price2num($tmpcal[1] - $obj-&amp;gt;total_tva, &#039;MT&#039;, 1);&lt;br /&gt;
        if ($diff)&lt;br /&gt;
        {&lt;br /&gt;
            $sqlfix=&amp;quot;UPDATE &amp;quot;.MAIN_DB_PREFIX.$this-&amp;gt;table_element_line.&amp;quot; SET &amp;quot;.$fieldtva.&amp;quot; = &amp;quot;.$tmpcal[1].&amp;quot;, total_ttc = &amp;quot;.$tmpcal[2].&amp;quot; WHERE rowid = &amp;quot;.$obj-&amp;gt;rowid;&lt;br /&gt;
            dol_syslog(&#039;We found unconsistent data into detailed line (difference of &#039;.$diff.&#039;) for line rowid = &#039;.$obj-&amp;gt;rowid.&amp;quot; (total vat of line calculated=&amp;quot;.$tmpcal[1].&amp;quot;, database=&amp;quot;.$obj-&amp;gt;total_tva.&amp;quot;). We fix the total_vat and total_ttc of line by running sqlfix = &amp;quot;.$sqlfix);&lt;br /&gt;
            $resqlfix=$this-&amp;gt;db-&amp;gt;query($sqlfix);&lt;br /&gt;
            if (! $resqlfix) dol_print_error($this-&amp;gt;db,&#039;Failed to update line&#039;);&lt;br /&gt;
            $obj-&amp;gt;total_tva = $tmpcal[1];&lt;br /&gt;
            $obj-&amp;gt;total_ttc = $tmpcal[2];&lt;br /&gt;
            //&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just after, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
$cf_coeff = $cf_records[$obj-&amp;gt;rowid]-&amp;gt;coefficient;&lt;br /&gt;
if (empty($cf_coeff) and $cf_coeff !== 0) $cf_coeff = 1; // by default, an empty coefficient = 1&lt;br /&gt;
$obj-&amp;gt;total_ht = $obj-&amp;gt;total_ht * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_tva = $obj-&amp;gt;total_tva * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_localtax1 = $obj-&amp;gt;total_localtax1 * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_localtax2 = $obj-&amp;gt;total_localtax2 * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_ttc = $obj-&amp;gt;total_ttc * $cf_coeff;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should do the trick. Now go to one of your invoices (or whatever module you are targetting), and try to add/edit a product/service line to force refresh the total price. You should then see this:&lt;br /&gt;
[[File:Cf_wiki_coeff_remaining_debt.png]]&lt;br /&gt;
&lt;br /&gt;
= Custom tax per product =&lt;br /&gt;
&lt;br /&gt;
This part will show you how to create a static custom tax per product. Static means that the tax will be stored per product, and not per product&#039;s line, thus you just have to setup the tax once per product and it will always be applied in every invoice you add this product.&lt;br /&gt;
&lt;br /&gt;
To do that, you will need the CustomFields Pro module + ProductsEasyAccess.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
1- Create a product&#039;s custom fields called &amp;quot;custom_tax&amp;quot;. Now, using ProductsEasyAccess, you can access this field wherever you wants, either to show it on the Dolibarr&#039;s interface (see previous chapter), or in a PDF template, which we will describe below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2- Modify your invoice&#039;s PDF template. You first need to load all the products&#039; fields &#039;&#039;before&#039;&#039; the loop over the products&#039; lines. To do this, search for a line like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// This should be placed above the &amp;quot;loop lines&amp;quot; below, add this:&lt;br /&gt;
&lt;br /&gt;
dol_include_once(&#039;/productseasyaccess/lib/productseasyaccess.lib.php&#039;);&lt;br /&gt;
fill_object_with_products_fields($object);&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines, search for this command&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  // Add this just below the for loop, this will ease your php manipulation after.&lt;br /&gt;
  // This will allow you to more easily access the products&#039; fields (this will store the id of each product inside $lineproductid).&lt;br /&gt;
  $line_product_id = $object-&amp;gt;lines[$i]-&amp;gt;fk_product;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4- You can now use your products&#039; fields in your PDF. To access the custom_tax field, just use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And to print it in your PDF:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell(0,3, $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax, 0, &#039;L&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5- Then, if you want to count the custom tax total and add it to your vat total, you can create a counter inside the for loop we cited in the first step:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_custom_tax = 0; // Initialize the custom tax total counter&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  $object-&amp;gt;total_custom_tax += $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then find inside the function **_tableau_tot**, find the following line:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And prepend the total modification like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc + $object-&amp;gt;total_custom_tax; // prepend this to add the custom tax to the total&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Done. This should show your custom tax per product and your total including all custom taxes.&lt;/div&gt;</summary>
		<author><name>Lrq3000</name></author>
	</entry>
	<entry>
		<id>https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33344</id>
		<title>Module CustomFields Cases</title>
		<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33344"/>
		<updated>2015-04-01T14:10:52Z</updated>

		<summary type="html">&lt;p&gt;Lrq3000: Add section Cascading&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:CustomFields]]&lt;br /&gt;
{{TemplateDocDevEn}}&lt;br /&gt;
{{TemplateModEN}}&lt;br /&gt;
{{BasculeDevUserEn|&lt;br /&gt;
name=CustomFields|&lt;br /&gt;
num=8500|&lt;br /&gt;
devdoc=[[Module_CustomFields_Dev]]|&lt;br /&gt;
userdoc=[[Module_CustomFields]]|}}&lt;br /&gt;
This page lists a compilation of concrete, practical examples uses of [[Module_CustomFields|CustomFields]] and of its various features.&lt;br /&gt;
&lt;br /&gt;
If you had an experience with CustomFields, you can also add here your case as an example for others to get inspired.&lt;br /&gt;
&lt;br /&gt;
= How to make a generic PDF template listing all custom fields =&lt;br /&gt;
&lt;br /&gt;
To make a generic PDF template listing all custom fields, you just need to take an existing PDF template, then you need to follow these 3 steps:&lt;br /&gt;
&lt;br /&gt;
1/ Edit the class name (required, else it will conflict with the original template).&lt;br /&gt;
&lt;br /&gt;
Eg: if we take the crabe template for invoices, we change:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;crabe&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
into:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_customfields extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;customfields&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2/ Copy this generic function at the end of the PDF:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propale/order/etc... (CustomFields simpler functions will automatically adapt)&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs); // set default PDF font size&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
    // Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
    $customfields = customfields_fill_object($object, null, $outputlangs, null, true);&lt;br /&gt;
&lt;br /&gt;
    // Setting the starting position of the text cursor&lt;br /&gt;
    $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
    $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
    // Printing the customfields&lt;br /&gt;
    foreach ($object-&amp;gt;customfields as $label=&amp;gt;$value) { // $value is already formatted!&lt;br /&gt;
        // Get translated label&lt;br /&gt;
        $translatedlabel = $customfields-&amp;gt;findLabelPDF($label, $outputlangs); // translated label of the customfield (not translated by default in customfields_fill_object() because a field should always be accessible by a base name, whatever the translation is)&lt;br /&gt;
&lt;br /&gt;
        // PDF formatting, placement and printing&lt;br /&gt;
        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
        $pdf-&amp;gt;MultiCell(0,3, $translatedlabel.&#039; ($object-&amp;gt;customfields-&amp;gt;&#039;.$label.&#039;): &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternative function (with a bit less functionalities, but more controllable):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page (used to debug if CustomFields setup is correct)&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propal/product/whatever...&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs);&lt;br /&gt;
&lt;br /&gt;
            if (empty($object-&amp;gt;table_element) or empty($object-&amp;gt;id)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;Current \$object is not compatible with CustomFields, could not find table_element or id.&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
    $customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element);&lt;br /&gt;
&lt;br /&gt;
            // Fetching custom fields records&lt;br /&gt;
            $fields = $customfields-&amp;gt;fetch($object-&amp;gt;id);&lt;br /&gt;
&lt;br /&gt;
            if (!isset($fields)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;No custom field could be found for this object. Please check your configuration (did you create at least one customfield and set a value in the current datasheet?)&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            } else {&lt;br /&gt;
                // Setting the starting position of the text cursor&lt;br /&gt;
                $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
                $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
                // Printing the customfields&lt;br /&gt;
                foreach ($fields as $key=&amp;gt;$field) {&lt;br /&gt;
                        $translatedname = $customfields-&amp;gt;findLabelPDF($key, $outputlangs); // label of the customfield&lt;br /&gt;
                        $value = $customfields-&amp;gt;simpleprintFieldPDF($key, $field, $outputlangs); // value (cleaned and properly formatted) of the customfield&lt;br /&gt;
&lt;br /&gt;
                        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
                        $pdf-&amp;gt;MultiCell(0,3, $translatedname.&#039;: &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
                        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3/ Call the _pagecustomfields function at the right place (generally, after _pagefoot and just before $pdf-&amp;gt;Close()):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Pied de page&lt;br /&gt;
$this-&amp;gt;_pagefoot($pdf,$object,$outputlangs);&lt;br /&gt;
$pdf-&amp;gt;AliasNbPages();&lt;br /&gt;
&lt;br /&gt;
// CustomFields&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) { // if the customfields module is activated...&lt;br /&gt;
        // Add a page with the customfields&lt;br /&gt;
        $pdf-&amp;gt;AddPage();&lt;br /&gt;
        $this-&amp;gt;_pagecustomfields($pdf,$object,$outputlangs);&lt;br /&gt;
        $pagenb++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
$pdf-&amp;gt;Close(); // you should first search for this, you should only find one occurrence&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then go to the module&#039;s admin panel (eg: invoice admin panel) and enable the template you&#039;ve just created.&lt;br /&gt;
&lt;br /&gt;
You can now use it to test if your customfields work.&lt;br /&gt;
&lt;br /&gt;
= Conditions on fields selection =&lt;br /&gt;
&lt;br /&gt;
Sometimes you may want to show a DropdownBox or Constraint (or another multi-choice type), but you may want to show or hide choices based on conditions (eg: only if it&#039;s a prospect, only if this zone was selected, only if this category was selected, etc..).&lt;br /&gt;
&lt;br /&gt;
To know which is the best solution you should choose to implement your condition, you should first know in which category your condition falls in.&lt;br /&gt;
&lt;br /&gt;
== Conditions categories ==&lt;br /&gt;
&lt;br /&gt;
Conditions can be categorized into 4 types:&lt;br /&gt;
&lt;br /&gt;
* static condition: choices are restrained on a condition that never changes (eg: only show third-parties that are suppliers, or lowercase all data on sql insertion, or check that a field is above a certain number, etc.).&lt;br /&gt;
* semi-dynamic condition: choices that are constrained on a static table but based on the value of another field/customfield. This is also commonly called &amp;quot;Cascaded dropdown lists&amp;quot; or &amp;quot;Dynamically linked dropdown lists&amp;quot;.&lt;br /&gt;
* dynamic: everything is dynamic: choices are based on a subfield of a field of the current module, so that there&#039;s no material table and it&#039;s impossible to create a view (because you would need to create one view per id) (eg: show only the contacts attached to the third-party being accessed)&lt;br /&gt;
* timed condition: a condition that is based on time or recurrence (eg: at 5:00 AM delete old records, etc..)&lt;br /&gt;
&lt;br /&gt;
== Conditions solutions ==&lt;br /&gt;
&lt;br /&gt;
Now that you know what kind of condition you want to set, you can choose one of the following solutions depending on your preferences and knowledge.&lt;br /&gt;
&lt;br /&gt;
In any case, you can always use the &#039;&#039;&#039;CustomFields&#039;s overload functions&#039;&#039;&#039; to fit your needs for nearly every type of condition.&lt;br /&gt;
&lt;br /&gt;
=== Static condition ===&lt;br /&gt;
&lt;br /&gt;
* Constraint WHERE clause (when creating/editing a custom field, you can specify your own WHERE conditions).&lt;br /&gt;
* View: create a view on the table based on the condition, and then create a custom field constrained on this view (which is just like any table), eg: only show third-parties that are suppliers:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
CREATE VIEW llx_societe_supplier_view AS&lt;br /&gt;
SELECT *&lt;br /&gt;
FROM llx_societe&lt;br /&gt;
WHERE fournisseur=1;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
WARNING: foreign keys (and thus constraints) can&#039;t be used on views with MySQL, and materialized views neither exist, so you can&#039;t use this method with MySQL.&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
* SQL Transformations&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
=== Semi-dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade option is ideal in most cases, and if not enough, you can use the Cascade Custom option and Overloading Functions.&lt;br /&gt;
* View&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
&lt;br /&gt;
=== Dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade Custom and Overload functions&lt;br /&gt;
* Recursive Remote Fields Access&lt;br /&gt;
* Making your own module, which calls the CustomFields class&lt;br /&gt;
&lt;br /&gt;
=== Timed condition ===&lt;br /&gt;
&lt;br /&gt;
* SQL scheduled events&lt;br /&gt;
* Cron job&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
= Cascading =&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Overloading functions =&lt;br /&gt;
&lt;br /&gt;
Here are a few concrete use cases of overloading functions to achieve various goals.&lt;br /&gt;
&lt;br /&gt;
== Dynamic list selection based on another one ==&lt;br /&gt;
&lt;br /&gt;
The goal is to show two custom fields on the Third-Party module: one which gives the zone (secteur) of the third-party (Isere, Alpes du sud, Haute-Savoie...) and the other which gives relative to the zone the list of all the ski resorts (station_a) inside the selected zone.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will show how to manually manage this kind of requirements using [[Module_CustomFields#Overloading_functions:_Adding_your_own_management_code_for_custom_fields|overloading functions]], but in latest CustomFields releases, the same solution can be achieved automatically using the [[Module_CustomFields#Cascade|Cascade option]].&lt;br /&gt;
&lt;br /&gt;
We have one table &#039;&#039;&#039;llx_k_station&#039;&#039;&#039; (rowid, station (char50), secteur(char50)) which contains the following: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1,les 2 alpes,isère&lt;br /&gt;
2,chamrousse,isère&lt;br /&gt;
3,alpe d&#039;huez,isère&lt;br /&gt;
4,vars,alpes du sud&lt;br /&gt;
5,risoul,alpes du sud&lt;br /&gt;
etc...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In CustomFields&#039;s admin panel, we create two custom fields:&lt;br /&gt;
&lt;br /&gt;
1- secteur: returns the list of all zones - type DropdownBox: enum(&#039;- Aucun&#039;,&#039;Alpes du Sud&#039;,&#039;export&#039;,&#039;Haute-Savoie&#039;,&#039;Isère&#039;, etc...)&lt;br /&gt;
&lt;br /&gt;
2- station_a: returns the list of all ski resorts - type constraint on &#039;&#039;&#039;llx_k_station&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Here is the code to put in customfields_fields_extend.lib.php that will allow to show only the ski resorts corresponding to the selected zone:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_editview_societe_station_a (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$parameters, &amp;amp;$action, &amp;amp;$user, &amp;amp;$idvar, &amp;amp;$rightok, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
  global $db; // allow to access database functions&lt;br /&gt;
  $sql=&amp;quot;SELECT llx_k_station.station, llx_k_station.rowid FROM llx_societe_customfields INNER JOIN llx_k_station ON llx_societe_customfields.Secteur = llx_k_station.secteur WHERE llx_societe_customfields.fk_societe=&amp;quot;.$object-&amp;gt;id;&lt;br /&gt;
$result=$db-&amp;gt;query($sql);&lt;br /&gt;
 &lt;br /&gt;
  if ($result) {&lt;br /&gt;
    $num = $db-&amp;gt;num_rows($result);&lt;br /&gt;
    $i = 0;&lt;br /&gt;
    $myarray = array();&lt;br /&gt;
    while ($i &amp;lt; $num)&lt;br /&gt;
    {&lt;br /&gt;
      $obj = $db-&amp;gt;fetch_object($result);&lt;br /&gt;
      $myarray []=array(&#039;id&#039;=&amp;gt;$obj-&amp;gt;rowid, &#039;value&#039;=&amp;gt;$obj-&amp;gt;station);&lt;br /&gt;
      $i++;&lt;br /&gt;
    }&lt;br /&gt;
    $value = $myarray; &lt;br /&gt;
    $db-&amp;gt;free($result);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to manub for giving this case.&lt;br /&gt;
&lt;br /&gt;
== Dynamic discount computation per products lines on propales ==&lt;br /&gt;
&lt;br /&gt;
The goal here is to compute the discount for a product line in propales based on a computation on three custom fields.&lt;br /&gt;
&lt;br /&gt;
First, create three custom fields for &amp;quot;Line commercial proposals&amp;quot;: vl, cpl and lc.&lt;br /&gt;
&lt;br /&gt;
Then add the following overloading function at the end of your customfields_fields_extend.lib.php file (read the comments for more informations):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_propaldet_vl (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
&lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $vl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;vl&#039;};&lt;br /&gt;
    $cpl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;cpl&#039;};&lt;br /&gt;
    $lc = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;lc&#039;};&lt;br /&gt;
&lt;br /&gt;
    // Compute the discount&lt;br /&gt;
    $remise_calculee = (100 - $vl) * (100 - $cpl) * (100 - $lc);&lt;br /&gt;
&lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $remise_calculee = price2num($remise_calculee);&lt;br /&gt;
&lt;br /&gt;
    // Assign the new discount into the PropalLine object&lt;br /&gt;
    $object-&amp;gt;remise_percent = $remise_calculee;&lt;br /&gt;
&lt;br /&gt;
    // Update the price (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
&lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
&lt;br /&gt;
    //$q = &amp;quot;UPDATE  &amp;quot;.MAIN_DB_PREFIX.&amp;quot;propaldet SET remise_percent=&amp;quot;.$remise_calculee.&amp;quot; WHERE  rowid=&amp;quot;.$object-&amp;gt;rowid;&lt;br /&gt;
    //$db-&amp;gt;query($q);&lt;br /&gt;
    &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;vl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;cpl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;lc&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Linking Dolibarr objects from two different modules =&lt;br /&gt;
&lt;br /&gt;
Dolibarr sometimes offers the possibility to link two different modules. Eg: when you convert an Intervention card into an Invoice.&lt;br /&gt;
&lt;br /&gt;
In this case, you might want in your templates to fetch the custom fields of the linked object. Eg: you generate a PDF from the Invoice, but you want to get the custom fields from the Intervention that was converted into the Invoice.&lt;br /&gt;
&lt;br /&gt;
The key here is to use the &#039;&#039;&#039;llx_element_element&#039;&#039;&#039; table or the &#039;&#039;&#039;llx_element_*&#039;&#039;&#039; tables (eg: llx_element_contact): this is a standard Dolibarr table that stores all links between objects of two different modules.&lt;br /&gt;
&lt;br /&gt;
Here is the structure:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
rowid | fk_source | sourcetype | fk_target | targettype&lt;br /&gt;
-------------------------------------------------------&lt;br /&gt;
1 | 2 | fichinter | 5 | facture&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can then use a php code like the following to populate your $object with customfields:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
// This will also create and returns us an instance of the CustomFields class, which we will use to manually query the llx_element_element table&lt;br /&gt;
$customfields = customfields_fill_object($object, null, $outputlangs, &#039;facture&#039;, true); // beautified values&lt;br /&gt;
&lt;br /&gt;
// Querying the llx_element_element table manually to get the link between the invoice and the linked intervention card (we want the id of the intervention card)&lt;br /&gt;
// fetchAny($columns, $table, $where=&#039;&#039;, $orderby=&#039;&#039;, $limitby=&#039;&#039;)&lt;br /&gt;
$eltrow = $customfields-&amp;gt;fetchAny(&#039;*&#039;, MAIN_DB_PREFIX.&#039;element_element&#039;, &#039;targettype=&amp;quot;&#039;.$object-&amp;gt;element.&#039;&amp;quot; and fk_target=&#039;.$object-&amp;gt;id.&#039; and sourcetype=&amp;quot;fichinter&amp;quot;&#039;);&lt;br /&gt;
&lt;br /&gt;
// Save the intervention&#039;s id&lt;br /&gt;
$sourceid = $eltrow[0][&#039;fk_source&#039;];&lt;br /&gt;
&lt;br /&gt;
// Crafting a dummy intervention object&lt;br /&gt;
$fromobject = new stdClass(); // Init an empty object&lt;br /&gt;
$fromobject-&amp;gt;id = $sourceid; // We need the id and..&lt;br /&gt;
$fromobject-&amp;gt;table_element = &#039;fichinter&#039;; // the table_element (name of the table)&lt;br /&gt;
&lt;br /&gt;
// We can then fill $object with the intervention custom fields (we store them in a sub property &#039;fichinter&#039; to avoid conflicts with invoice&#039;s custom fields)&lt;br /&gt;
customfields_fill_object($object, $fromobject, $outputlangs, &#039;fichinter&#039;, true);&lt;br /&gt;
&lt;br /&gt;
// Now you can access invoices custom fields using:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;facture-&amp;gt;cf_myfield);&lt;br /&gt;
// And access intervention custom fields with:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;fichinter-&amp;gt;cf_myfield);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to netassopro for the tip.&lt;br /&gt;
&lt;br /&gt;
= Modify the total global price with a coefficient (without tampering the discount) =&lt;br /&gt;
&lt;br /&gt;
This is more troublesome than modifying the total price per product/service line, because the total global price of an object (let&#039;s say an invoice, but this also works for any other object) is always recomputed by summing the prices and quantity of each product/service line. This means that although there&#039;s an entry &amp;quot;total_ttc&amp;quot; in the database to store the invoice&#039;s total price, this price is in fact always recomputed, it&#039;s never used as-is. Thus, it&#039;s useless to change only the database record: we need to inject some code to change how the total price is computed.&lt;br /&gt;
&lt;br /&gt;
This could be done using an overloading function or a trigger, but unluckily, Dolibarr doesn&#039;t call a trigger nor a hook when recomputing the price. We thus need to edit core files to do what we want.&lt;br /&gt;
&lt;br /&gt;
However, the bright side is that we can cleverly edit just two functions from one core file: update_price() and getMarginInfos() from /htdocs/core/class/commonobject.class.php , and everything should run smoothly. Beware that if you update Dolibarr, you will need to redo these two modifications in the code.&lt;br /&gt;
&lt;br /&gt;
First, you need to create a new custom field called &amp;quot;global_coefficient&amp;quot; in Invoices (not Invoices Lines!). Then you can continue to the subchapters below.&lt;br /&gt;
&lt;br /&gt;
Also note that this global coefficient is also compatible with per line coefficients (as described in the next chapter), thus you can use a global coefficient and per line coefficients at the same time. You can also use multiple global coefficient if you wish.&lt;br /&gt;
&lt;br /&gt;
Another note: if you have a grid of discounts depending on quantity or price or whatever parameter, you can program a precomputation code using a Custom Ajax function (this way, your discounts will be automatically computed, but then you can still manually enter them for particular cases since the custom field is editable).&lt;br /&gt;
&lt;br /&gt;
== Implement the change of global price in most of Dolibarr (including remaining debt to pay and PDF and ODT) ==&lt;br /&gt;
&lt;br /&gt;
The change we will implement here will reflect the new total price with the global coefficient in most of Dolibarr&#039;s code, since we will here change the main function that is usually called to update and get the total price.&lt;br /&gt;
&lt;br /&gt;
Open /htdocs/core/class/commonobject.class.php and look for the function update_price().&lt;br /&gt;
&lt;br /&gt;
You should see the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function update_price($exclspec=0,$roundingadjust=&#039;none&#039;,$nodatabaseupdate=0,$seller=&#039;&#039;)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just below the include_once line, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;;&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/conf/conf_customfields_func.lib.php&#039;;&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $this-&amp;gt;table_element);&lt;br /&gt;
// Get object&#039;s id (either stored in property &amp;quot;id&amp;quot; or &amp;quot;rowid&amp;quot;)&lt;br /&gt;
$this_id = (!empty($this-&amp;gt;id)) ? $this-&amp;gt;id : $this-&amp;gt;rowid;&lt;br /&gt;
// Load custom fields records&lt;br /&gt;
$cf_parent_record = $customfields-&amp;gt;fetch($this_id);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will load the global_coefficient custom field (and also any other custom field you have defined for invoices).&lt;br /&gt;
&lt;br /&gt;
Now scroll below, and you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
while ($i &amp;lt; $num)&lt;br /&gt;
{&lt;br /&gt;
    $obj = $this-&amp;gt;db-&amp;gt;fetch_object($resql);&lt;br /&gt;
&lt;br /&gt;
    ... (lots of code)&lt;br /&gt;
&lt;br /&gt;
    i++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Add revenue stamp to total&lt;br /&gt;
$this-&amp;gt;total_ttc       += isset($this-&amp;gt;revenuestamp)?$this-&amp;gt;revenuestamp:0;&lt;br /&gt;
&lt;br /&gt;
$this-&amp;gt;db-&amp;gt;free($resql);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is the end of the loop that is summing each product/service line price to compute the total price. Just after this loop, you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Now update global field total_ht, total_ttc and tva&lt;br /&gt;
$fieldht=&#039;total_ht&#039;;&lt;br /&gt;
$fieldtva=&#039;tva&#039;;&lt;br /&gt;
$fieldlocaltax1=&#039;localtax1&#039;;&lt;br /&gt;
$fieldlocaltax2=&#039;localtax2&#039;;&lt;br /&gt;
$fieldttc=&#039;total_ttc&#039;;&lt;br /&gt;
// Specific code for backward compatibility with old field names&lt;br /&gt;
if ($this-&amp;gt;element == &#039;facture&#039; || $this-&amp;gt;element == &#039;facturerec&#039;)             $fieldht=&#039;total&#039;;&lt;br /&gt;
if ($this-&amp;gt;element == &#039;facture_fourn&#039; || $this-&amp;gt;element == &#039;invoice_supplier&#039;) $fieldtva=&#039;total_tva&#039;;&lt;br /&gt;
if ($this-&amp;gt;element == &#039;propal&#039;)                                                $fieldttc=&#039;total&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Between those two blocks of code (so just above // Now update global field total_ht, total_ttc and tva), you can place the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
if (!empty($cf_parent_record-&amp;gt;global_coefficient)) { // avoid applying the coefficient if empty (this will return a 0 price)&lt;br /&gt;
    $this-&amp;gt;total_ht = $this-&amp;gt;total_ht * $cf_parent_record-&amp;gt;global_coefficient;&lt;br /&gt;
    $this-&amp;gt;total_tva = $this-&amp;gt;total_tva * $cf_parent_record-&amp;gt;global_coefficient;&lt;br /&gt;
    $this-&amp;gt;total_ttc = $this-&amp;gt;total_ttc * $cf_parent_record-&amp;gt;global_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Done, now try to add/edit a product/service line, and the total price should be affected by the global coefficient.&lt;br /&gt;
&lt;br /&gt;
Note that this modification &#039;&#039;&#039;needs only to be done once for all modules&#039;&#039;&#039;, you then just have to create a custom field named &amp;quot;global_coefficient&amp;quot; in every module you want to apply this coefficient.&lt;br /&gt;
&lt;br /&gt;
== Implementing the change of price in margin module ==&lt;br /&gt;
&lt;br /&gt;
If you enabled the margin module, you should see that the coefficient is not applied there, but it&#039;s easy to fix that.&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/class/commonobject.class.php and find the function getMarginInfos(), then inside find the following lines:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach($this-&amp;gt;lines as $line) {&lt;br /&gt;
    if (empty($line-&amp;gt;pa_ht) &amp;amp;&amp;amp; isset($line-&amp;gt;fk_fournprice) &amp;amp;&amp;amp; !$force_price) {&lt;br /&gt;
        $product = new ProductFournisseur($this-&amp;gt;db);&lt;br /&gt;
        if ($product-&amp;gt;fetch_product_fournisseur_price($line-&amp;gt;fk_fournprice))&lt;br /&gt;
            $line-&amp;gt;pa_ht = $product-&amp;gt;fourn_unitprice * (1 - $product-&amp;gt;fourn_remise_percent / 100);&lt;br /&gt;
        if (isset($conf-&amp;gt;global-&amp;gt;MARGIN_TYPE) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;MARGIN_TYPE == &amp;quot;2&amp;quot; &amp;amp;&amp;amp; $product-&amp;gt;fourn_unitcharges &amp;gt; 0)&lt;br /&gt;
            $line-&amp;gt;pa_ht += $product-&amp;gt;fourn_unitcharges;&lt;br /&gt;
    }&lt;br /&gt;
    // si prix d&#039;achat non renseigné et devrait l&#039;être, alors prix achat = prix vente&lt;br /&gt;
    if ((!isset($line-&amp;gt;pa_ht) || $line-&amp;gt;pa_ht == 0) &amp;amp;&amp;amp; $line-&amp;gt;subprice &amp;gt; 0 &amp;amp;&amp;amp; (isset($conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull == 1)) {&lt;br /&gt;
        $line-&amp;gt;pa_ht = $line-&amp;gt;subprice * (1 - ($line-&amp;gt;remise_percent / 100));&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the lines are filled with margin infos. The price has just been fetched here, and we will now modify it with the coefficient before the price gets used for margin computation.&lt;br /&gt;
&lt;br /&gt;
To optimize things up, we will load the custom fields before the loop (because they don&#039;t change between product/service lines, they are the custom fields of the parent object), and then apply the global_coefficient inside the loop.&lt;br /&gt;
&lt;br /&gt;
Just above the code we found (just before the for loop), paste the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
// Get object&#039;s id (either stored in property &amp;quot;id&amp;quot; or &amp;quot;rowid&amp;quot;)&lt;br /&gt;
$this_id = (!empty($this-&amp;gt;id)) ? $this-&amp;gt;id : $this-&amp;gt;rowid;&lt;br /&gt;
// Load custom fields records&lt;br /&gt;
$customfields = customfields_fill_object($this);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And now, just below the code we found, inside the loop (and just above the comment: &amp;quot;// calcul des marges&amp;quot;), paste the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
// Apply the coefficient&lt;br /&gt;
if (!empty($this-&amp;gt;customfields-&amp;gt;cf_global_coefficient)) {&lt;br /&gt;
    $line-&amp;gt;pa_ht = $line-&amp;gt;pa_ht * $this-&amp;gt;customfields-&amp;gt;cf_global_coefficient;&lt;br /&gt;
    $line-&amp;gt;subprice = $line-&amp;gt;subprice * $this-&amp;gt;customfields-&amp;gt;cf_global_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that here we cheated a bit: we do not compute the coefficient on the global total price, but rather per product/service line price. This is done on purpose, because mathematically this should produce no difference, and it also allows Dolibarr to continue peacefully with its margin calculations (else we would have to adapt all those computations, which would imply a lot more of code changes!).&lt;br /&gt;
&lt;br /&gt;
That&#039;s all, the price should be reflected in the margin. Just refresh the page to reflect the change.&lt;br /&gt;
&lt;br /&gt;
Note that this modification &#039;&#039;&#039;needs only to be done once for all modules&#039;&#039;&#039;, you then just have to create a custom field named &amp;quot;global_coefficient&amp;quot; in every module you want to apply this coefficient.&lt;br /&gt;
&lt;br /&gt;
== Refresh the price instantly ==&lt;br /&gt;
&lt;br /&gt;
Maybe you noticed one glitch: when you modify the global_coefficient, the global total price isn&#039;t updated until you add/edit a product/service line. This is because the update_price() function is only called when we add/edit a product/service line.&lt;br /&gt;
&lt;br /&gt;
To fix that, we need now to force the refreshing of price. To do that, we will use an &amp;quot;aftersave&amp;quot; overloading function, which will execute commands after our custom field is saved.&lt;br /&gt;
&lt;br /&gt;
Go to the folder /htdocs/customfields/fields and rename the file customfields_fields_extend.lib.php.example into customfields_fields_extend.lib.php .&lt;br /&gt;
&lt;br /&gt;
Now, open it in your favourite text editor, and add the following function :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_aftersave_facture_global_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value, $fields) {&lt;br /&gt;
    // Force refreshing total price&lt;br /&gt;
    $object-&amp;gt;update_price(0, &#039;auto&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s all. Now, when you edit your global_coefficient, this will force refresh the total price.&lt;br /&gt;
&lt;br /&gt;
However, you will still notice one last glitch: when you edit the global_coefficient, the total price isn&#039;t reflected instantly, you must refresh the webpage once to see the new price. This is because of how the datasheet and the CustomFields hook are designed: CustomFields is called only exactly where the custom fields are place, thus it is called after the total price, the margin and the remaining debt are printed. Thus, CustomFields updates the value of your custom field only after all those informations get printed, and thus it cannot modify them at this stage. That&#039;s why those information are out-of-date, and you must refresh the webpage once to get to see the latest values.&lt;br /&gt;
&lt;br /&gt;
There is a workaround for this: refresh automatically the webpage (but delete the POST data to avoid an infinite loop). You just need to use this overloading code instead of the one above:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_aftersave_facture_global_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value, $fields) {&lt;br /&gt;
    // Force refreshing total price&lt;br /&gt;
    $object-&amp;gt;update_price(0, &#039;auto&#039;);&lt;br /&gt;
    // Force refresh the page to update prices that were printed before the custom field aftersave was triggered (and only if not creating the object because then there&#039;s no need to refresh)&lt;br /&gt;
    if ($action !== &#039;add&#039;) print(&#039;&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;window.location.href = window.location.pathname + window.location.search;&amp;lt;/script&amp;gt;&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Another way to fix this issue would be to modify how CustomFields save the custom fields, by moving this stage at the very top. However, this would need another hook, which should be called at the very beginning of the page loading, which should be made only for this purpose (PageLoading), and should be available in all modules supported by CustomFields in order to not break anything.&lt;br /&gt;
&lt;br /&gt;
Note that you will need to create one overloading function for each module you want to support with the global_coefficient custom field. For example, if you want to support both invoices and commercial proposals, you will need two overloading functions:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient for invoices (note the &amp;quot;facture&amp;quot; in the name of the function)&lt;br /&gt;
function customfields_field_aftersave_facture_global_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value, $fields) {&lt;br /&gt;
    // Force refreshing total price&lt;br /&gt;
    $object-&amp;gt;update_price(0, &#039;auto&#039;);&lt;br /&gt;
    // Force refresh the page to update prices that were printed before the custom field aftersave was triggered (and only if not creating the object because then there&#039;s no need to refresh)&lt;br /&gt;
    if ($action !== &#039;add&#039;) print(&#039;&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;window.location.href = window.location.pathname + window.location.search;&amp;lt;/script&amp;gt;&#039;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Global coefficient for commercial proposals (note the &amp;quot;propal&amp;quot; in the name of the function)&lt;br /&gt;
function customfields_field_aftersave_propal_global_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value, $fields) {&lt;br /&gt;
    // Force refreshing total price&lt;br /&gt;
    $object-&amp;gt;update_price(0, &#039;auto&#039;);&lt;br /&gt;
    // Force refresh the page to update prices that were printed before the custom field aftersave was triggered (and only if not creating the object because then there&#039;s no need to refresh)&lt;br /&gt;
    if ($action !== &#039;add&#039;) print(&#039;&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;window.location.href = window.location.pathname + window.location.search;&amp;lt;/script&amp;gt;&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Modify the total price PER product/service line with a coefficient (without tampering the discount) =&lt;br /&gt;
&lt;br /&gt;
Here we will go through the creation and management of a custom field called &#039;&#039;&#039;coefficient&#039;&#039;&#039; that will change the total price of each product by modifying it by the coefficient.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will not create a coefficient per product but rather per product&#039;s line. Thus if you want to create a coefficient that never change between invoices but only depending on the product and stays static, please refer to the next chapter below about creating a custom tax per product.&lt;br /&gt;
&lt;br /&gt;
The preliminary and necessary step is to first create a custom field that will be used to modify the total price. We will create one for Client Invoice Lines.&lt;br /&gt;
&lt;br /&gt;
Go to the CustomFields administration panel, then into the Client Invoice Lines tab, then click on the button New Field, name the field &#039;&#039;&#039;coefficient&#039;&#039;&#039;, and set the type to &#039;&#039;&#039;Double&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
When you go to client invoices in Dolibarr, you should then see something similar to this:&lt;br /&gt;
[[File:Cfe1.jpg]]&lt;br /&gt;
You can see that our coefficient field was successfully created.&lt;br /&gt;
&lt;br /&gt;
Then here are two alternative ways of implementing the change of the total price of each product relativery to this coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change total price in database ==&lt;br /&gt;
&lt;br /&gt;
Here we will use a &amp;quot;save&amp;quot; overloading function to change the total price directly in the database, thus we won&#039;t have to modify anything else (eg: nor PDF templating nor ODT) since the total price will be already updated in the database.&lt;br /&gt;
&lt;br /&gt;
Warning: this method will work only with CustomFields =&amp;gt; 3.2.23 since the &amp;quot;save&amp;quot; overloading function did not work for products lines before.&lt;br /&gt;
&lt;br /&gt;
Just add the following overloading function in your customfields_fields_extend.lib.php file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_facturedet_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
 &lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $coefficient = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;coefficient&#039;}; // you could also use $coefficient = $value;&lt;br /&gt;
 &lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $coefficient = price2num($coefficient);&lt;br /&gt;
 &lt;br /&gt;
    // Get the original subprices (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
 &lt;br /&gt;
    // Update the subprices with the coefficient&lt;br /&gt;
    $object-&amp;gt;total_ht = $object-&amp;gt;total_ht * $coefficient; // compute no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva = $object-&amp;gt;total_tva * $coefficient; // compute tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc * $coefficient; // compute tax included price&lt;br /&gt;
 &lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(&#039;&#039;,true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
 &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;coefficient&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should show the correct total price on the Dolibarr&#039;s interface without modifying the unit subprice (P.U. Unit):&lt;br /&gt;
&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
You have nothing to edit in your PDF or ODT template, since the new subprice with applied coefficient will be automatically printed instead of the old one without coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change printed total price per product without tampering the subprice in the database ==&lt;br /&gt;
&lt;br /&gt;
Here is a way to change the printed total price (the one shown on screen and in PDF/ODT) but without changing the total price in the database.&lt;br /&gt;
&lt;br /&gt;
The following sub-chapters are all independent, thus you can only follow the ones that you require, eg: if you only need to change the price in your PDF documents, just create a custom field then jump to the sub-chapter to &#039;&#039;&#039;Implement the change of price in PDF&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in ODT templates ===&lt;br /&gt;
&lt;br /&gt;
There&#039;s no eval implementation currently inside ODT templates, but there is a kind of hook implementation, called [[Create_an_ODT_document_template#Other_personalized_tags|substitution functions]].&lt;br /&gt;
&lt;br /&gt;
You can either create your own module, then create your own substitution function, but for the sake of simplicity in this tutorial, we will directly use the CustomFields substitution function. However, please remember that you will have to redo your changes everytime you will update the CustomFields module, so if you don&#039;t want to do that it would be better if you create your own module and substitution function.&lt;br /&gt;
&lt;br /&gt;
Open the file &#039;&#039;&#039;htdocs/customfields/core/substitutions/functions_customfields.lib.php&#039;&#039;&#039; and go to the function &#039;&#039;&#039;customfields_completesubstitutionarray_lines&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
At the very end of this function, but inside the customfields check:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) {&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
But just before the ending bracket, you should add the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($object-&amp;gt;table_element_line === &#039;facturedet&#039;) { // First check that we are in the right module, we don&#039;t want to use coefficient for other modules if we didn&#039;t define the custom field there&lt;br /&gt;
    $substitutionarray[&#039;line_price_full&#039;] = $substitutionarray[&#039;line_price_ttc&#039;] * $substitutionarray[&#039;cf_coefficient&#039;]; // multiply the total price with our coefficient&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code will add a new tag &#039;&#039;&#039;{line_price_full}&#039;&#039;&#039; that you can then use inside your ODT templates.&lt;br /&gt;
&lt;br /&gt;
Just place this tag in a table in your ODT document, between the table tags ( [!-- BEGIN row.lines --] ... [!-- END row.lines --] ), and this will print your full price.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in PDF templates ===&lt;br /&gt;
&lt;br /&gt;
First, we will create our own PDF template by copying an existing template, and then we will edit it to change the price of items.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Creating your own PDF template by copying an existing template&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Go into the following folder: &#039;&#039;&#039;htdocs/core/modules/facture/doc/&#039;&#039;&#039;, then copy the file &#039;&#039;&#039;pdf_crabe.modules.php&#039;&#039;&#039; and name the new one &#039;&#039;&#039;pdf_crabecoeff.modules.php&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Then open it in your favourite editor, and change the following lines:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures // change pdf_crabe into pdf_crabecoeff&lt;br /&gt;
{&lt;br /&gt;
...&lt;br /&gt;
    function __construct($db)&lt;br /&gt;
    {&lt;br /&gt;
        ...&lt;br /&gt;
	$this-&amp;gt;name = &amp;quot;crabe&amp;quot;; // change &amp;quot;crabe&amp;quot; into &amp;quot;crabecoeff&amp;quot;&lt;br /&gt;
        ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you have your own PDF template!&lt;br /&gt;
&lt;br /&gt;
Now you should &#039;&#039;&#039;enable your PDF template&#039;&#039;&#039; before doing anything else, by going into the Modules Administration panel, and into the Invoice admin page, then click on the ON button at the right of the &#039;&#039;&#039;crabecoeff&#039;&#039;&#039; entry to enable the template.&lt;br /&gt;
&lt;br /&gt;
Now, let&#039;s proceed onto modifying the prices.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Changing the prices automatically in your PDF&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
You can now do pretty much anything you want in your PDF template, but I will give you a mean to automatically change the prices of every products with our coefficient, as well as the total price, without editing too much the template.&lt;br /&gt;
&lt;br /&gt;
First, try to find the following code of line (should be inside the write_file() method):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf=pdf_getInstance($this-&amp;gt;format);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, just copy/paste the following code &#039;&#039;&#039;below&#039;&#039;&#039; pdf_getInstance():&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$this-&amp;gt;customfields = customfields_fill_object($object, null, $outputlangs, null, true); // beautified values&lt;br /&gt;
$this-&amp;gt;customfields_raw = customfields_fill_object($object, null, $outputlangs, &#039;raw&#039;, null); // raw values&lt;br /&gt;
$this-&amp;gt;customfields_lines = customfields_fill_object_lines($object, null, $outputlangs, null, true); // product lines&#039; values&lt;br /&gt;
&lt;br /&gt;
// Resetting every $object price because we will compute them anew&lt;br /&gt;
$object-&amp;gt;total_ht = 0;&lt;br /&gt;
$object-&amp;gt;total_tva = 0;&lt;br /&gt;
$object-&amp;gt;total_ttc = 0;&lt;br /&gt;
// For each line of product/service, we will recompute all the prices&lt;br /&gt;
foreach($object-&amp;gt;lines as $line) {&lt;br /&gt;
    // Get the coefficient custom field&#039;s value for the current line&lt;br /&gt;
    $coeff = $object-&amp;gt;customfields-&amp;gt;lines-&amp;gt;{$line-&amp;gt;rowid}-&amp;gt;cf_coefficient;&lt;br /&gt;
    // Recompute the price of this product/service line&lt;br /&gt;
    $line-&amp;gt;total_ht = $line-&amp;gt;total_ht*$coeff; // compute no tax price&lt;br /&gt;
    $line-&amp;gt;total_tva = $line-&amp;gt;total_tva*$coeff; // compute tax amount&lt;br /&gt;
    $line-&amp;gt;total_ttc = $line-&amp;gt;total_ttc*$coeff; // compute tax included price&lt;br /&gt;
    // Do the sum of all new products/services prices and tva (will be incremented with every product/service line)&lt;br /&gt;
    $object-&amp;gt;total_ht += $line-&amp;gt;total_ht; // global no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva += $line-&amp;gt;total_tva; // global vat/tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc += $line-&amp;gt;total_ttc; // global  tax included price&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you should try to generate your PDF document, and you should get something like this:&lt;br /&gt;
[[File:Cfe3.jpg]]&lt;br /&gt;
&lt;br /&gt;
Note: printing the coefficient as a column in your PDF document is also possible but will not be covered here because PDF templates will vary for every persons depending on your needs.&lt;br /&gt;
For guidelines about how to do this, you can read the documentation on [[Create_a_PDF_document_template|how to create a PDF template]] and [[Module_CustomFields#Implementing_in_PDF_templates|how to use custom fields in PDF templates]].&lt;br /&gt;
&lt;br /&gt;
Hint: you should be looking inside the following conditional block, inside the write_file() method:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
{&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of total price in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
Unluckily there is not (yet) any hooking context to modify how lines of products/services are printed, thus the only thing you can do is directly edit the &#039;&#039;&#039;template file&#039;&#039;&#039; and manually put inside your code to load your custom field and print it the way you want using HTML formatting, for our example:&lt;br /&gt;
&lt;br /&gt;
* For Dolibarr 3.3 and above, edit the file &#039;&#039;&#039;htdocs/core/tpl/objectline_view.tpl.php&#039;&#039;&#039;&lt;br /&gt;
* For Dolibarr 3.1 and 3.2, edit the file &#039;&#039;&#039;htdocs/core/tpl/freeproductline_view.tpl.php&#039;&#039;&#039; AND &#039;&#039;&#039;predefinedproductline_view.tpl.php&#039;&#039;&#039; (apply the same changes to both files)&lt;br /&gt;
&lt;br /&gt;
In the template file you opened, find the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And replace it by:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $object-&amp;gt;table_element_line; // you can use $object-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
&lt;br /&gt;
// Modifying the HT price and printing it on the Dolibarr&#039;s datasheet interface&lt;br /&gt;
// Applying a multiplying/dividing coefficient to an HT price will give a mathematically correct TTC price since the taxes are also a multiplier&lt;br /&gt;
// However, if you want to apply other mathematical operations (like addition or substaction), you should use the total price with $line-&amp;gt;total_ttc&lt;br /&gt;
if (!empty($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
    echo price($line-&amp;gt;total_ht * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient);&lt;br /&gt;
} else {&lt;br /&gt;
    echo price($line-&amp;gt;total_ht);&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should produce something like this:&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Technical note: here we are using customfields_fill_object() instead of customfields_fill_object_lines() (the latter being specifically adapted to lines and thus should theoretically be used here) for performance reasons because the template file we are editing is called in a loop, and customfields_fill_object_lines() should be called outside a loop (because it already does the job that is done by the loop), so here we &#039;tricks&#039; the customfields_fill_object() function to avoid this loop by submitting &#039;facturedet&#039; as a module by itself (when in fact it&#039;s a product lines table, but it works the same way nevertheless). This is a good demonstration of the flexibility of the CustomFields functions and methods.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the coefficient column in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
It&#039;s also possible to show the coefficient in a new column on the Dolibarr interface.&lt;br /&gt;
&lt;br /&gt;
To do that, you need to do two things:&lt;br /&gt;
&lt;br /&gt;
1- Add the column title &amp;quot;Coeff.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
2- Add the generator that will input the coefficient value for each line.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Add the column title &amp;quot;Coeff.&amp;quot;&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
Open in your favourite editor the file /htdocs/core/class/commonobject.class.php and inside the function printObjectLines (note the plural s), find the following lines of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Total HT&lt;br /&gt;
print &#039;&amp;lt;td align=&amp;quot;right&amp;quot; width=&amp;quot;50&amp;quot;&amp;gt;&#039;.$langs-&amp;gt;trans(&#039;TotalHTShort&#039;).&#039;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is what prints the Total HT price column. We will place the Coeff column just before.&lt;br /&gt;
&lt;br /&gt;
So, just above this code, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
dol_include_once(&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element_line);&lt;br /&gt;
$coeff_field = $customfields-&amp;gt;fetchFieldStruct(&#039;coefficient&#039;);&lt;br /&gt;
if (!empty($coeff_field)) {&lt;br /&gt;
    print &#039;&amp;lt;td align=&amp;quot;right&amp;quot; width=&amp;quot;50&amp;quot;&amp;gt;&#039;.$langs-&amp;gt;trans(&#039;Coeff.&#039;).&#039;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will take care of showing the Coeff. column, but only for modules where you created such a custom field (because the file we modified is a common object for all modules in Dolibarr, not just invoices, thus this modification will gracefully skip this custom field if it does not exists for other modules).&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Add the generator&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/tpl/objectline_view.tpl.php and try to find the following block of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php if ($line-&amp;gt;special_code == 3)	{ ?&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo $langs-&amp;gt;trans(&#039;Option&#039;); ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } else { ?&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is what prints the total HT price value for each line.&lt;br /&gt;
&lt;br /&gt;
Just above of this block of code, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $object-&amp;gt;table_element_line; // you can use $this-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
if (isset($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
    &amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient; ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just like for the column title, this code will take care of generating the coefficient value only if the custom field is defined, because this file is also a generic template for all modules of Dolibarr.&lt;br /&gt;
&lt;br /&gt;
At this point, everything is done, you should see the following:&lt;br /&gt;
[[File:Cf_wiki_coeff_column.png]]&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in margin module ===&lt;br /&gt;
&lt;br /&gt;
If you enabled the margin module, you should see that the coefficient is not applied there, but it&#039;s easy to fix that.&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/class/commonobject.class.php and find the function getMarginInfos(), then inside find the following lines:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach($this-&amp;gt;lines as $line) {&lt;br /&gt;
    if (empty($line-&amp;gt;pa_ht) &amp;amp;&amp;amp; isset($line-&amp;gt;fk_fournprice) &amp;amp;&amp;amp; !$force_price) {&lt;br /&gt;
        $product = new ProductFournisseur($this-&amp;gt;db);&lt;br /&gt;
        if ($product-&amp;gt;fetch_product_fournisseur_price($line-&amp;gt;fk_fournprice))&lt;br /&gt;
            $line-&amp;gt;pa_ht = $product-&amp;gt;fourn_unitprice * (1 - $product-&amp;gt;fourn_remise_percent / 100);&lt;br /&gt;
        if (isset($conf-&amp;gt;global-&amp;gt;MARGIN_TYPE) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;MARGIN_TYPE == &amp;quot;2&amp;quot; &amp;amp;&amp;amp; $product-&amp;gt;fourn_unitcharges &amp;gt; 0)&lt;br /&gt;
            $line-&amp;gt;pa_ht += $product-&amp;gt;fourn_unitcharges;&lt;br /&gt;
    }&lt;br /&gt;
    // si prix d&#039;achat non renseigné et devrait l&#039;être, alors prix achat = prix vente&lt;br /&gt;
    if ((!isset($line-&amp;gt;pa_ht) || $line-&amp;gt;pa_ht == 0) &amp;amp;&amp;amp; $line-&amp;gt;subprice &amp;gt; 0 &amp;amp;&amp;amp; (isset($conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull == 1)) {&lt;br /&gt;
        $line-&amp;gt;pa_ht = $line-&amp;gt;subprice * (1 - ($line-&amp;gt;remise_percent / 100));&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the lines are filled with margin infos. The price has just been fetched here, and we will now modify it with the coefficient before the price gets used for margin computation.&lt;br /&gt;
&lt;br /&gt;
Just below the code we found, paste the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $line-&amp;gt;table_element;&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
if (!empty($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
    $line-&amp;gt;pa_ht = $line-&amp;gt;pa_ht * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient;&lt;br /&gt;
    $line-&amp;gt;subprice = $line-&amp;gt;subprice * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s all, the price should be reflected in the margin.&lt;br /&gt;
[[File:Cf_wiki_coeff_margin.png]]&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in remaining debt to pay ===&lt;br /&gt;
&lt;br /&gt;
Open /htdocs/core/class/commonobject.class.php and look for the function update_price().&lt;br /&gt;
&lt;br /&gt;
You should see the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function update_price($exclspec=0,$roundingadjust=&#039;none&#039;,$nodatabaseupdate=0,$seller=&#039;&#039;)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just below the include_once line, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;;&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/conf/conf_customfields_func.lib.php&#039;;&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $this-&amp;gt;table_element_line);&lt;br /&gt;
// Fetching line ids from parent object id&lt;br /&gt;
$line_ids = $customfields-&amp;gt;fetchAny(&#039;rowid&#039;, MAIN_DB_PREFIX.$this-&amp;gt;table_element_line, $this-&amp;gt;fk_element.&#039; = &#039;.$this-&amp;gt;id);&lt;br /&gt;
$line_ids = array_values_recursive(&#039;rowid&#039;, $line_ids);&lt;br /&gt;
// Load custom fields&lt;br /&gt;
$cf_records = $customfields-&amp;gt;fetch($line_ids);&lt;br /&gt;
// Reassociate the records with the line id (instead of the customfields record id)&lt;br /&gt;
$cf_line_cfrowid_to_rowid = array_reassociate(&#039;rowid&#039;, &#039;fk_&#039;.$this-&amp;gt;table_element_line, $cf_records);&lt;br /&gt;
$tmp = $cf_records;&lt;br /&gt;
$cf_records = array();&lt;br /&gt;
foreach ($tmp as $k=&amp;gt;$v) { $cf_records[$cf_line_cfrowid_to_rowid[$k]] = $tmp[$k]; }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will load the coefficient custom field (and also any other custom field you have defined for lines).&lt;br /&gt;
&lt;br /&gt;
Now scroll below, and you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
while ($i &amp;lt; $num)&lt;br /&gt;
{&lt;br /&gt;
    $obj = $this-&amp;gt;db-&amp;gt;fetch_object($resql);&lt;br /&gt;
&lt;br /&gt;
    // Note: There is no check on detail line and no check on total, if $forcedroundingmode = &#039;none&#039;&lt;br /&gt;
&lt;br /&gt;
    if ($forcedroundingmode == &#039;0&#039;)	// Check if data on line are consistent. This may solve lines that were not consistent because set with $forcedroundingmode=&#039;auto&#039;&lt;br /&gt;
    {&lt;br /&gt;
        $localtax_array=array($obj-&amp;gt;localtax1_type,$obj-&amp;gt;localtax1_tx,$obj-&amp;gt;localtax2_type,$obj-&amp;gt;localtax2_tx);&lt;br /&gt;
        $tmpcal=calcul_price_total($obj-&amp;gt;qty, $obj-&amp;gt;up, $obj-&amp;gt;remise_percent, $obj-&amp;gt;vatrate, $obj-&amp;gt;localtax1_tx, $obj-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $obj-&amp;gt;info_bits, $obj-&amp;gt;product_type, $seller, $localtax_array);&lt;br /&gt;
        $diff=price2num($tmpcal[1] - $obj-&amp;gt;total_tva, &#039;MT&#039;, 1);&lt;br /&gt;
        if ($diff)&lt;br /&gt;
        {&lt;br /&gt;
            $sqlfix=&amp;quot;UPDATE &amp;quot;.MAIN_DB_PREFIX.$this-&amp;gt;table_element_line.&amp;quot; SET &amp;quot;.$fieldtva.&amp;quot; = &amp;quot;.$tmpcal[1].&amp;quot;, total_ttc = &amp;quot;.$tmpcal[2].&amp;quot; WHERE rowid = &amp;quot;.$obj-&amp;gt;rowid;&lt;br /&gt;
            dol_syslog(&#039;We found unconsistent data into detailed line (difference of &#039;.$diff.&#039;) for line rowid = &#039;.$obj-&amp;gt;rowid.&amp;quot; (total vat of line calculated=&amp;quot;.$tmpcal[1].&amp;quot;, database=&amp;quot;.$obj-&amp;gt;total_tva.&amp;quot;). We fix the total_vat and total_ttc of line by running sqlfix = &amp;quot;.$sqlfix);&lt;br /&gt;
            $resqlfix=$this-&amp;gt;db-&amp;gt;query($sqlfix);&lt;br /&gt;
            if (! $resqlfix) dol_print_error($this-&amp;gt;db,&#039;Failed to update line&#039;);&lt;br /&gt;
            $obj-&amp;gt;total_tva = $tmpcal[1];&lt;br /&gt;
            $obj-&amp;gt;total_ttc = $tmpcal[2];&lt;br /&gt;
            //&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just after, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
$cf_coeff = $cf_records[$obj-&amp;gt;rowid]-&amp;gt;coefficient;&lt;br /&gt;
if (empty($cf_coeff) and $cf_coeff !== 0) $cf_coeff = 1; // by default, an empty coefficient = 1&lt;br /&gt;
$obj-&amp;gt;total_ht = $obj-&amp;gt;total_ht * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_tva = $obj-&amp;gt;total_tva * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_localtax1 = $obj-&amp;gt;total_localtax1 * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_localtax2 = $obj-&amp;gt;total_localtax2 * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_ttc = $obj-&amp;gt;total_ttc * $cf_coeff;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should do the trick. Now go to one of your invoices (or whatever module you are targetting), and try to add/edit a product/service line to force refresh the total price. You should then see this:&lt;br /&gt;
[[File:Cf_wiki_coeff_remaining_debt.png]]&lt;br /&gt;
&lt;br /&gt;
= Custom tax per product =&lt;br /&gt;
&lt;br /&gt;
This part will show you how to create a static custom tax per product. Static means that the tax will be stored per product, and not per product&#039;s line, thus you just have to setup the tax once per product and it will always be applied in every invoice you add this product.&lt;br /&gt;
&lt;br /&gt;
To do that, you will need the CustomFields Pro module + ProductsEasyAccess.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
1- Create a product&#039;s custom fields called &amp;quot;custom_tax&amp;quot;. Now, using ProductsEasyAccess, you can access this field wherever you wants, either to show it on the Dolibarr&#039;s interface (see previous chapter), or in a PDF template, which we will describe below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2- Modify your invoice&#039;s PDF template. You first need to load all the products&#039; fields &#039;&#039;before&#039;&#039; the loop over the products&#039; lines. To do this, search for a line like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// This should be placed above the &amp;quot;loop lines&amp;quot; below, add this:&lt;br /&gt;
&lt;br /&gt;
dol_include_once(&#039;/productseasyaccess/lib/productseasyaccess.lib.php&#039;);&lt;br /&gt;
fill_object_with_products_fields($object);&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines, search for this command&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  // Add this just below the for loop, this will ease your php manipulation after.&lt;br /&gt;
  // This will allow you to more easily access the products&#039; fields (this will store the id of each product inside $lineproductid).&lt;br /&gt;
  $line_product_id = $object-&amp;gt;lines[$i]-&amp;gt;fk_product;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4- You can now use your products&#039; fields in your PDF. To access the custom_tax field, just use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And to print it in your PDF:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell(0,3, $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax, 0, &#039;L&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5- Then, if you want to count the custom tax total and add it to your vat total, you can create a counter inside the for loop we cited in the first step:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_custom_tax = 0; // Initialize the custom tax total counter&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  $object-&amp;gt;total_custom_tax += $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then find inside the function **_tableau_tot**, find the following line:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And prepend the total modification like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc + $object-&amp;gt;total_custom_tax; // prepend this to add the custom tax to the total&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Done. This should show your custom tax per product and your total including all custom taxes.&lt;/div&gt;</summary>
		<author><name>Lrq3000</name></author>
	</entry>
	<entry>
		<id>https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33182</id>
		<title>Module CustomFields Cases</title>
		<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33182"/>
		<updated>2015-02-24T16:03:15Z</updated>

		<summary type="html">&lt;p&gt;Lrq3000: /* Static condition */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:CustomFields]]&lt;br /&gt;
{{TemplateDocDevEn}}&lt;br /&gt;
{{TemplateModEN}}&lt;br /&gt;
{{BasculeDevUserEn|&lt;br /&gt;
name=CustomFields|&lt;br /&gt;
num=8500|&lt;br /&gt;
devdoc=[[Module_CustomFields_Dev]]|&lt;br /&gt;
userdoc=[[Module_CustomFields]]|}}&lt;br /&gt;
This page lists a compilation of concrete, practical examples uses of [[Module_CustomFields|CustomFields]] and of its various features.&lt;br /&gt;
&lt;br /&gt;
If you had an experience with CustomFields, you can also add here your case as an example for others to get inspired.&lt;br /&gt;
&lt;br /&gt;
= How to make a generic PDF template listing all custom fields =&lt;br /&gt;
&lt;br /&gt;
To make a generic PDF template listing all custom fields, you just need to take an existing PDF template, then you need to follow these 3 steps:&lt;br /&gt;
&lt;br /&gt;
1/ Edit the class name (required, else it will conflict with the original template).&lt;br /&gt;
&lt;br /&gt;
Eg: if we take the crabe template for invoices, we change:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;crabe&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
into:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_customfields extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;customfields&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2/ Copy this generic function at the end of the PDF:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propale/order/etc... (CustomFields simpler functions will automatically adapt)&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs); // set default PDF font size&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
    // Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
    $customfields = customfields_fill_object($object, null, $outputlangs, null, true);&lt;br /&gt;
&lt;br /&gt;
    // Setting the starting position of the text cursor&lt;br /&gt;
    $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
    $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
    // Printing the customfields&lt;br /&gt;
    foreach ($object-&amp;gt;customfields as $label=&amp;gt;$value) { // $value is already formatted!&lt;br /&gt;
        // Get translated label&lt;br /&gt;
        $translatedlabel = $customfields-&amp;gt;findLabelPDF($label, $outputlangs); // translated label of the customfield (not translated by default in customfields_fill_object() because a field should always be accessible by a base name, whatever the translation is)&lt;br /&gt;
&lt;br /&gt;
        // PDF formatting, placement and printing&lt;br /&gt;
        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
        $pdf-&amp;gt;MultiCell(0,3, $translatedlabel.&#039; ($object-&amp;gt;customfields-&amp;gt;&#039;.$label.&#039;): &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternative function (with a bit less functionalities, but more controllable):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page (used to debug if CustomFields setup is correct)&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propal/product/whatever...&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs);&lt;br /&gt;
&lt;br /&gt;
            if (empty($object-&amp;gt;table_element) or empty($object-&amp;gt;id)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;Current \$object is not compatible with CustomFields, could not find table_element or id.&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
    $customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element);&lt;br /&gt;
&lt;br /&gt;
            // Fetching custom fields records&lt;br /&gt;
            $fields = $customfields-&amp;gt;fetch($object-&amp;gt;id);&lt;br /&gt;
&lt;br /&gt;
            if (!isset($fields)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;No custom field could be found for this object. Please check your configuration (did you create at least one customfield and set a value in the current datasheet?)&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            } else {&lt;br /&gt;
                // Setting the starting position of the text cursor&lt;br /&gt;
                $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
                $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
                // Printing the customfields&lt;br /&gt;
                foreach ($fields as $key=&amp;gt;$field) {&lt;br /&gt;
                        $translatedname = $customfields-&amp;gt;findLabelPDF($key, $outputlangs); // label of the customfield&lt;br /&gt;
                        $value = $customfields-&amp;gt;simpleprintFieldPDF($key, $field, $outputlangs); // value (cleaned and properly formatted) of the customfield&lt;br /&gt;
&lt;br /&gt;
                        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
                        $pdf-&amp;gt;MultiCell(0,3, $translatedname.&#039;: &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
                        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3/ Call the _pagecustomfields function at the right place (generally, after _pagefoot and just before $pdf-&amp;gt;Close()):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Pied de page&lt;br /&gt;
$this-&amp;gt;_pagefoot($pdf,$object,$outputlangs);&lt;br /&gt;
$pdf-&amp;gt;AliasNbPages();&lt;br /&gt;
&lt;br /&gt;
// CustomFields&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) { // if the customfields module is activated...&lt;br /&gt;
        // Add a page with the customfields&lt;br /&gt;
        $pdf-&amp;gt;AddPage();&lt;br /&gt;
        $this-&amp;gt;_pagecustomfields($pdf,$object,$outputlangs);&lt;br /&gt;
        $pagenb++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
$pdf-&amp;gt;Close(); // you should first search for this, you should only find one occurrence&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then go to the module&#039;s admin panel (eg: invoice admin panel) and enable the template you&#039;ve just created.&lt;br /&gt;
&lt;br /&gt;
You can now use it to test if your customfields work.&lt;br /&gt;
&lt;br /&gt;
= Conditions on fields selection =&lt;br /&gt;
&lt;br /&gt;
Sometimes you may want to show a DropdownBox or Constraint (or another multi-choice type), but you may want to show or hide choices based on conditions (eg: only if it&#039;s a prospect, only if this zone was selected, only if this category was selected, etc..).&lt;br /&gt;
&lt;br /&gt;
To know which is the best solution you should choose to implement your condition, you should first know in which category your condition falls in.&lt;br /&gt;
&lt;br /&gt;
== Conditions categories ==&lt;br /&gt;
&lt;br /&gt;
Conditions can be categorized into 4 types:&lt;br /&gt;
&lt;br /&gt;
* static condition: choices are restrained on a condition that never changes (eg: only show third-parties that are suppliers, or lowercase all data on sql insertion, or check that a field is above a certain number, etc.).&lt;br /&gt;
* semi-dynamic condition: choices that are constrained on a static table but based on the value of another field/customfield. This is also commonly called &amp;quot;Cascaded dropdown lists&amp;quot; or &amp;quot;Dynamically linked dropdown lists&amp;quot;.&lt;br /&gt;
* dynamic: everything is dynamic: choices are based on a subfield of a field of the current module, so that there&#039;s no material table and it&#039;s impossible to create a view (because you would need to create one view per id) (eg: show only the contacts attached to the third-party being accessed)&lt;br /&gt;
* timed condition: a condition that is based on time or recurrence (eg: at 5:00 AM delete old records, etc..)&lt;br /&gt;
&lt;br /&gt;
== Conditions solutions ==&lt;br /&gt;
&lt;br /&gt;
Now that you know what kind of condition you want to set, you can choose one of the following solutions depending on your preferences and knowledge.&lt;br /&gt;
&lt;br /&gt;
In any case, you can always use the &#039;&#039;&#039;CustomFields&#039;s overload functions&#039;&#039;&#039; to fit your needs for nearly every type of condition.&lt;br /&gt;
&lt;br /&gt;
=== Static condition ===&lt;br /&gt;
&lt;br /&gt;
* Constraint WHERE clause (when creating/editing a custom field, you can specify your own WHERE conditions).&lt;br /&gt;
* View: create a view on the table based on the condition, and then create a custom field constrained on this view (which is just like any table), eg: only show third-parties that are suppliers:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
CREATE VIEW llx_societe_supplier_view AS&lt;br /&gt;
SELECT *&lt;br /&gt;
FROM llx_societe&lt;br /&gt;
WHERE fournisseur=1;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
WARNING: foreign keys (and thus constraints) can&#039;t be used on views with MySQL, and materialized views neither exist, so you can&#039;t use this method with MySQL.&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
* SQL Transformations&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
=== Semi-dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade option is ideal in most cases, and if not enough, you can use the Cascade Custom option and Overloading Functions.&lt;br /&gt;
* View&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
&lt;br /&gt;
=== Dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade Custom and Overload functions&lt;br /&gt;
* Recursive Remote Fields Access&lt;br /&gt;
* Making your own module, which calls the CustomFields class&lt;br /&gt;
&lt;br /&gt;
=== Timed condition ===&lt;br /&gt;
&lt;br /&gt;
* SQL scheduled events&lt;br /&gt;
* Cron job&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
= Overloading functions =&lt;br /&gt;
&lt;br /&gt;
Here are a few concrete use cases of overloading functions to achieve various goals.&lt;br /&gt;
&lt;br /&gt;
== Dynamic list selection based on another one ==&lt;br /&gt;
&lt;br /&gt;
The goal is to show two custom fields on the Third-Party module: one which gives the zone (secteur) of the third-party (Isere, Alpes du sud, Haute-Savoie...) and the other which gives relative to the zone the list of all the ski resorts (station_a) inside the selected zone.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will show how to manually manage this kind of requirements using [[Module_CustomFields#Overloading_functions:_Adding_your_own_management_code_for_custom_fields|overloading functions]], but in latest CustomFields releases, the same solution can be achieved automatically using the [[Module_CustomFields#Cascade|Cascade option]].&lt;br /&gt;
&lt;br /&gt;
We have one table &#039;&#039;&#039;llx_k_station&#039;&#039;&#039; (rowid, station (char50), secteur(char50)) which contains the following: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1,les 2 alpes,isère&lt;br /&gt;
2,chamrousse,isère&lt;br /&gt;
3,alpe d&#039;huez,isère&lt;br /&gt;
4,vars,alpes du sud&lt;br /&gt;
5,risoul,alpes du sud&lt;br /&gt;
etc...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In CustomFields&#039;s admin panel, we create two custom fields:&lt;br /&gt;
&lt;br /&gt;
1- secteur: returns the list of all zones - type DropdownBox: enum(&#039;- Aucun&#039;,&#039;Alpes du Sud&#039;,&#039;export&#039;,&#039;Haute-Savoie&#039;,&#039;Isère&#039;, etc...)&lt;br /&gt;
&lt;br /&gt;
2- station_a: returns the list of all ski resorts - type constraint on &#039;&#039;&#039;llx_k_station&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Here is the code to put in customfields_fields_extend.lib.php that will allow to show only the ski resorts corresponding to the selected zone:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_editview_societe_station_a (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$parameters, &amp;amp;$action, &amp;amp;$user, &amp;amp;$idvar, &amp;amp;$rightok, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
  global $db; // allow to access database functions&lt;br /&gt;
  $sql=&amp;quot;SELECT llx_k_station.station, llx_k_station.rowid FROM llx_societe_customfields INNER JOIN llx_k_station ON llx_societe_customfields.Secteur = llx_k_station.secteur WHERE llx_societe_customfields.fk_societe=&amp;quot;.$object-&amp;gt;id;&lt;br /&gt;
$result=$db-&amp;gt;query($sql);&lt;br /&gt;
 &lt;br /&gt;
  if ($result) {&lt;br /&gt;
    $num = $db-&amp;gt;num_rows($result);&lt;br /&gt;
    $i = 0;&lt;br /&gt;
    $myarray = array();&lt;br /&gt;
    while ($i &amp;lt; $num)&lt;br /&gt;
    {&lt;br /&gt;
      $obj = $db-&amp;gt;fetch_object($result);&lt;br /&gt;
      $myarray []=array(&#039;id&#039;=&amp;gt;$obj-&amp;gt;rowid, &#039;value&#039;=&amp;gt;$obj-&amp;gt;station);&lt;br /&gt;
      $i++;&lt;br /&gt;
    }&lt;br /&gt;
    $value = $myarray; &lt;br /&gt;
    $db-&amp;gt;free($result);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to manub for giving this case.&lt;br /&gt;
&lt;br /&gt;
== Dynamic discount computation per products lines on propales ==&lt;br /&gt;
&lt;br /&gt;
The goal here is to compute the discount for a product line in propales based on a computation on three custom fields.&lt;br /&gt;
&lt;br /&gt;
First, create three custom fields for &amp;quot;Line commercial proposals&amp;quot;: vl, cpl and lc.&lt;br /&gt;
&lt;br /&gt;
Then add the following overloading function at the end of your customfields_fields_extend.lib.php file (read the comments for more informations):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_propaldet_vl (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
&lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $vl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;vl&#039;};&lt;br /&gt;
    $cpl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;cpl&#039;};&lt;br /&gt;
    $lc = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;lc&#039;};&lt;br /&gt;
&lt;br /&gt;
    // Compute the discount&lt;br /&gt;
    $remise_calculee = (100 - $vl) * (100 - $cpl) * (100 - $lc);&lt;br /&gt;
&lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $remise_calculee = price2num($remise_calculee);&lt;br /&gt;
&lt;br /&gt;
    // Assign the new discount into the PropalLine object&lt;br /&gt;
    $object-&amp;gt;remise_percent = $remise_calculee;&lt;br /&gt;
&lt;br /&gt;
    // Update the price (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
&lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
&lt;br /&gt;
    //$q = &amp;quot;UPDATE  &amp;quot;.MAIN_DB_PREFIX.&amp;quot;propaldet SET remise_percent=&amp;quot;.$remise_calculee.&amp;quot; WHERE  rowid=&amp;quot;.$object-&amp;gt;rowid;&lt;br /&gt;
    //$db-&amp;gt;query($q);&lt;br /&gt;
    &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;vl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;cpl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;lc&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Linking Dolibarr objects from two different modules =&lt;br /&gt;
&lt;br /&gt;
Dolibarr sometimes offers the possibility to link two different modules. Eg: when you convert an Intervention card into an Invoice.&lt;br /&gt;
&lt;br /&gt;
In this case, you might want in your templates to fetch the custom fields of the linked object. Eg: you generate a PDF from the Invoice, but you want to get the custom fields from the Intervention that was converted into the Invoice.&lt;br /&gt;
&lt;br /&gt;
The key here is to use the &#039;&#039;&#039;llx_element_element&#039;&#039;&#039; table or the &#039;&#039;&#039;llx_element_*&#039;&#039;&#039; tables (eg: llx_element_contact): this is a standard Dolibarr table that stores all links between objects of two different modules.&lt;br /&gt;
&lt;br /&gt;
Here is the structure:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
rowid | fk_source | sourcetype | fk_target | targettype&lt;br /&gt;
-------------------------------------------------------&lt;br /&gt;
1 | 2 | fichinter | 5 | facture&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can then use a php code like the following to populate your $object with customfields:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
// This will also create and returns us an instance of the CustomFields class, which we will use to manually query the llx_element_element table&lt;br /&gt;
$customfields = customfields_fill_object($object, null, $outputlangs, &#039;facture&#039;, true); // beautified values&lt;br /&gt;
&lt;br /&gt;
// Querying the llx_element_element table manually to get the link between the invoice and the linked intervention card (we want the id of the intervention card)&lt;br /&gt;
// fetchAny($columns, $table, $where=&#039;&#039;, $orderby=&#039;&#039;, $limitby=&#039;&#039;)&lt;br /&gt;
$eltrow = $customfields-&amp;gt;fetchAny(&#039;*&#039;, MAIN_DB_PREFIX.&#039;element_element&#039;, &#039;targettype=&amp;quot;&#039;.$object-&amp;gt;element.&#039;&amp;quot; and fk_target=&#039;.$object-&amp;gt;id.&#039; and sourcetype=&amp;quot;fichinter&amp;quot;&#039;);&lt;br /&gt;
&lt;br /&gt;
// Save the intervention&#039;s id&lt;br /&gt;
$sourceid = $eltrow[0][&#039;fk_source&#039;];&lt;br /&gt;
&lt;br /&gt;
// Crafting a dummy intervention object&lt;br /&gt;
$fromobject = new stdClass(); // Init an empty object&lt;br /&gt;
$fromobject-&amp;gt;id = $sourceid; // We need the id and..&lt;br /&gt;
$fromobject-&amp;gt;table_element = &#039;fichinter&#039;; // the table_element (name of the table)&lt;br /&gt;
&lt;br /&gt;
// We can then fill $object with the intervention custom fields (we store them in a sub property &#039;fichinter&#039; to avoid conflicts with invoice&#039;s custom fields)&lt;br /&gt;
customfields_fill_object($object, $fromobject, $outputlangs, &#039;fichinter&#039;, true);&lt;br /&gt;
&lt;br /&gt;
// Now you can access invoices custom fields using:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;facture-&amp;gt;cf_myfield);&lt;br /&gt;
// And access intervention custom fields with:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;fichinter-&amp;gt;cf_myfield);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to netassopro for the tip.&lt;br /&gt;
&lt;br /&gt;
= Modify the total global price with a coefficient (without tampering the discount) =&lt;br /&gt;
&lt;br /&gt;
This is more troublesome than modifying the total price per product/service line, because the total global price of an object (let&#039;s say an invoice, but this also works for any other object) is always recomputed by summing the prices and quantity of each product/service line. This means that although there&#039;s an entry &amp;quot;total_ttc&amp;quot; in the database to store the invoice&#039;s total price, this price is in fact always recomputed, it&#039;s never used as-is. Thus, it&#039;s useless to change only the database record: we need to inject some code to change how the total price is computed.&lt;br /&gt;
&lt;br /&gt;
This could be done using an overloading function or a trigger, but unluckily, Dolibarr doesn&#039;t call a trigger nor a hook when recomputing the price. We thus need to edit core files to do what we want.&lt;br /&gt;
&lt;br /&gt;
However, the bright side is that we can cleverly edit just two functions from one core file: update_price() and getMarginInfos() from /htdocs/core/class/commonobject.class.php , and everything should run smoothly. Beware that if you update Dolibarr, you will need to redo these two modifications in the code.&lt;br /&gt;
&lt;br /&gt;
First, you need to create a new custom field called &amp;quot;global_coefficient&amp;quot; in Invoices (not Invoices Lines!). Then you can continue to the subchapters below.&lt;br /&gt;
&lt;br /&gt;
Also note that this global coefficient is also compatible with per line coefficients (as described in the next chapter), thus you can use a global coefficient and per line coefficients at the same time. You can also use multiple global coefficient if you wish.&lt;br /&gt;
&lt;br /&gt;
Another note: if you have a grid of discounts depending on quantity or price or whatever parameter, you can program a precomputation code using a Custom Ajax function (this way, your discounts will be automatically computed, but then you can still manually enter them for particular cases since the custom field is editable).&lt;br /&gt;
&lt;br /&gt;
== Implement the change of global price in most of Dolibarr (including remaining debt to pay and PDF and ODT) ==&lt;br /&gt;
&lt;br /&gt;
The change we will implement here will reflect the new total price with the global coefficient in most of Dolibarr&#039;s code, since we will here change the main function that is usually called to update and get the total price.&lt;br /&gt;
&lt;br /&gt;
Open /htdocs/core/class/commonobject.class.php and look for the function update_price().&lt;br /&gt;
&lt;br /&gt;
You should see the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function update_price($exclspec=0,$roundingadjust=&#039;none&#039;,$nodatabaseupdate=0,$seller=&#039;&#039;)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just below the include_once line, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;;&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/conf/conf_customfields_func.lib.php&#039;;&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $this-&amp;gt;table_element);&lt;br /&gt;
// Get object&#039;s id (either stored in property &amp;quot;id&amp;quot; or &amp;quot;rowid&amp;quot;)&lt;br /&gt;
$this_id = (!empty($this-&amp;gt;id)) ? $this-&amp;gt;id : $this-&amp;gt;rowid;&lt;br /&gt;
// Load custom fields records&lt;br /&gt;
$cf_parent_record = $customfields-&amp;gt;fetch($this_id);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will load the global_coefficient custom field (and also any other custom field you have defined for invoices).&lt;br /&gt;
&lt;br /&gt;
Now scroll below, and you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
while ($i &amp;lt; $num)&lt;br /&gt;
{&lt;br /&gt;
    $obj = $this-&amp;gt;db-&amp;gt;fetch_object($resql);&lt;br /&gt;
&lt;br /&gt;
    ... (lots of code)&lt;br /&gt;
&lt;br /&gt;
    i++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Add revenue stamp to total&lt;br /&gt;
$this-&amp;gt;total_ttc       += isset($this-&amp;gt;revenuestamp)?$this-&amp;gt;revenuestamp:0;&lt;br /&gt;
&lt;br /&gt;
$this-&amp;gt;db-&amp;gt;free($resql);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is the end of the loop that is summing each product/service line price to compute the total price. Just after this loop, you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Now update global field total_ht, total_ttc and tva&lt;br /&gt;
$fieldht=&#039;total_ht&#039;;&lt;br /&gt;
$fieldtva=&#039;tva&#039;;&lt;br /&gt;
$fieldlocaltax1=&#039;localtax1&#039;;&lt;br /&gt;
$fieldlocaltax2=&#039;localtax2&#039;;&lt;br /&gt;
$fieldttc=&#039;total_ttc&#039;;&lt;br /&gt;
// Specific code for backward compatibility with old field names&lt;br /&gt;
if ($this-&amp;gt;element == &#039;facture&#039; || $this-&amp;gt;element == &#039;facturerec&#039;)             $fieldht=&#039;total&#039;;&lt;br /&gt;
if ($this-&amp;gt;element == &#039;facture_fourn&#039; || $this-&amp;gt;element == &#039;invoice_supplier&#039;) $fieldtva=&#039;total_tva&#039;;&lt;br /&gt;
if ($this-&amp;gt;element == &#039;propal&#039;)                                                $fieldttc=&#039;total&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Between those two blocks of code (so just above // Now update global field total_ht, total_ttc and tva), you can place the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
if (!empty($cf_parent_record-&amp;gt;global_coefficient)) { // avoid applying the coefficient if empty (this will return a 0 price)&lt;br /&gt;
    $this-&amp;gt;total_ht = $this-&amp;gt;total_ht * $cf_parent_record-&amp;gt;global_coefficient;&lt;br /&gt;
    $this-&amp;gt;total_tva = $this-&amp;gt;total_tva * $cf_parent_record-&amp;gt;global_coefficient;&lt;br /&gt;
    $this-&amp;gt;total_ttc = $this-&amp;gt;total_ttc * $cf_parent_record-&amp;gt;global_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Done, now try to add/edit a product/service line, and the total price should be affected by the global coefficient.&lt;br /&gt;
&lt;br /&gt;
Note that this modification &#039;&#039;&#039;needs only to be done once for all modules&#039;&#039;&#039;, you then just have to create a custom field named &amp;quot;global_coefficient&amp;quot; in every module you want to apply this coefficient.&lt;br /&gt;
&lt;br /&gt;
== Implementing the change of price in margin module ==&lt;br /&gt;
&lt;br /&gt;
If you enabled the margin module, you should see that the coefficient is not applied there, but it&#039;s easy to fix that.&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/class/commonobject.class.php and find the function getMarginInfos(), then inside find the following lines:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach($this-&amp;gt;lines as $line) {&lt;br /&gt;
    if (empty($line-&amp;gt;pa_ht) &amp;amp;&amp;amp; isset($line-&amp;gt;fk_fournprice) &amp;amp;&amp;amp; !$force_price) {&lt;br /&gt;
        $product = new ProductFournisseur($this-&amp;gt;db);&lt;br /&gt;
        if ($product-&amp;gt;fetch_product_fournisseur_price($line-&amp;gt;fk_fournprice))&lt;br /&gt;
            $line-&amp;gt;pa_ht = $product-&amp;gt;fourn_unitprice * (1 - $product-&amp;gt;fourn_remise_percent / 100);&lt;br /&gt;
        if (isset($conf-&amp;gt;global-&amp;gt;MARGIN_TYPE) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;MARGIN_TYPE == &amp;quot;2&amp;quot; &amp;amp;&amp;amp; $product-&amp;gt;fourn_unitcharges &amp;gt; 0)&lt;br /&gt;
            $line-&amp;gt;pa_ht += $product-&amp;gt;fourn_unitcharges;&lt;br /&gt;
    }&lt;br /&gt;
    // si prix d&#039;achat non renseigné et devrait l&#039;être, alors prix achat = prix vente&lt;br /&gt;
    if ((!isset($line-&amp;gt;pa_ht) || $line-&amp;gt;pa_ht == 0) &amp;amp;&amp;amp; $line-&amp;gt;subprice &amp;gt; 0 &amp;amp;&amp;amp; (isset($conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull == 1)) {&lt;br /&gt;
        $line-&amp;gt;pa_ht = $line-&amp;gt;subprice * (1 - ($line-&amp;gt;remise_percent / 100));&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the lines are filled with margin infos. The price has just been fetched here, and we will now modify it with the coefficient before the price gets used for margin computation.&lt;br /&gt;
&lt;br /&gt;
To optimize things up, we will load the custom fields before the loop (because they don&#039;t change between product/service lines, they are the custom fields of the parent object), and then apply the global_coefficient inside the loop.&lt;br /&gt;
&lt;br /&gt;
Just above the code we found (just before the for loop), paste the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
// Get object&#039;s id (either stored in property &amp;quot;id&amp;quot; or &amp;quot;rowid&amp;quot;)&lt;br /&gt;
$this_id = (!empty($this-&amp;gt;id)) ? $this-&amp;gt;id : $this-&amp;gt;rowid;&lt;br /&gt;
// Load custom fields records&lt;br /&gt;
$customfields = customfields_fill_object($this);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And now, just below the code we found, inside the loop (and just above the comment: &amp;quot;// calcul des marges&amp;quot;), paste the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
// Apply the coefficient&lt;br /&gt;
if (!empty($this-&amp;gt;customfields-&amp;gt;cf_global_coefficient)) {&lt;br /&gt;
    $line-&amp;gt;pa_ht = $line-&amp;gt;pa_ht * $this-&amp;gt;customfields-&amp;gt;cf_global_coefficient;&lt;br /&gt;
    $line-&amp;gt;subprice = $line-&amp;gt;subprice * $this-&amp;gt;customfields-&amp;gt;cf_global_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that here we cheated a bit: we do not compute the coefficient on the global total price, but rather per product/service line price. This is done on purpose, because mathematically this should produce no difference, and it also allows Dolibarr to continue peacefully with its margin calculations (else we would have to adapt all those computations, which would imply a lot more of code changes!).&lt;br /&gt;
&lt;br /&gt;
That&#039;s all, the price should be reflected in the margin. Just refresh the page to reflect the change.&lt;br /&gt;
&lt;br /&gt;
Note that this modification &#039;&#039;&#039;needs only to be done once for all modules&#039;&#039;&#039;, you then just have to create a custom field named &amp;quot;global_coefficient&amp;quot; in every module you want to apply this coefficient.&lt;br /&gt;
&lt;br /&gt;
== Refresh the price instantly ==&lt;br /&gt;
&lt;br /&gt;
Maybe you noticed one glitch: when you modify the global_coefficient, the global total price isn&#039;t updated until you add/edit a product/service line. This is because the update_price() function is only called when we add/edit a product/service line.&lt;br /&gt;
&lt;br /&gt;
To fix that, we need now to force the refreshing of price. To do that, we will use an &amp;quot;aftersave&amp;quot; overloading function, which will execute commands after our custom field is saved.&lt;br /&gt;
&lt;br /&gt;
Go to the folder /htdocs/customfields/fields and rename the file customfields_fields_extend.lib.php.example into customfields_fields_extend.lib.php .&lt;br /&gt;
&lt;br /&gt;
Now, open it in your favourite text editor, and add the following function :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_aftersave_facture_global_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value, $fields) {&lt;br /&gt;
    // Force refreshing total price&lt;br /&gt;
    $object-&amp;gt;update_price(0, &#039;auto&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s all. Now, when you edit your global_coefficient, this will force refresh the total price.&lt;br /&gt;
&lt;br /&gt;
However, you will still notice one last glitch: when you edit the global_coefficient, the total price isn&#039;t reflected instantly, you must refresh the webpage once to see the new price. This is because of how the datasheet and the CustomFields hook are designed: CustomFields is called only exactly where the custom fields are place, thus it is called after the total price, the margin and the remaining debt are printed. Thus, CustomFields updates the value of your custom field only after all those informations get printed, and thus it cannot modify them at this stage. That&#039;s why those information are out-of-date, and you must refresh the webpage once to get to see the latest values.&lt;br /&gt;
&lt;br /&gt;
There is a workaround for this: refresh automatically the webpage (but delete the POST data to avoid an infinite loop). You just need to use this overloading code instead of the one above:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_aftersave_facture_global_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value, $fields) {&lt;br /&gt;
    // Force refreshing total price&lt;br /&gt;
    $object-&amp;gt;update_price(0, &#039;auto&#039;);&lt;br /&gt;
    // Force refresh the page to update prices that were printed before the custom field aftersave was triggered (and only if not creating the object because then there&#039;s no need to refresh)&lt;br /&gt;
    if ($action !== &#039;add&#039;) print(&#039;&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;window.location.href = window.location.pathname + window.location.search;&amp;lt;/script&amp;gt;&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Another way to fix this issue would be to modify how CustomFields save the custom fields, by moving this stage at the very top. However, this would need another hook, which should be called at the very beginning of the page loading, which should be made only for this purpose (PageLoading), and should be available in all modules supported by CustomFields in order to not break anything.&lt;br /&gt;
&lt;br /&gt;
Note that you will need to create one overloading function for each module you want to support with the global_coefficient custom field. For example, if you want to support both invoices and commercial proposals, you will need two overloading functions:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient for invoices (note the &amp;quot;facture&amp;quot; in the name of the function)&lt;br /&gt;
function customfields_field_aftersave_facture_global_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value, $fields) {&lt;br /&gt;
    // Force refreshing total price&lt;br /&gt;
    $object-&amp;gt;update_price(0, &#039;auto&#039;);&lt;br /&gt;
    // Force refresh the page to update prices that were printed before the custom field aftersave was triggered (and only if not creating the object because then there&#039;s no need to refresh)&lt;br /&gt;
    if ($action !== &#039;add&#039;) print(&#039;&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;window.location.href = window.location.pathname + window.location.search;&amp;lt;/script&amp;gt;&#039;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Global coefficient for commercial proposals (note the &amp;quot;propal&amp;quot; in the name of the function)&lt;br /&gt;
function customfields_field_aftersave_propal_global_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value, $fields) {&lt;br /&gt;
    // Force refreshing total price&lt;br /&gt;
    $object-&amp;gt;update_price(0, &#039;auto&#039;);&lt;br /&gt;
    // Force refresh the page to update prices that were printed before the custom field aftersave was triggered (and only if not creating the object because then there&#039;s no need to refresh)&lt;br /&gt;
    if ($action !== &#039;add&#039;) print(&#039;&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;window.location.href = window.location.pathname + window.location.search;&amp;lt;/script&amp;gt;&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Modify the total price PER product/service line with a coefficient (without tampering the discount) =&lt;br /&gt;
&lt;br /&gt;
Here we will go through the creation and management of a custom field called &#039;&#039;&#039;coefficient&#039;&#039;&#039; that will change the total price of each product by modifying it by the coefficient.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will not create a coefficient per product but rather per product&#039;s line. Thus if you want to create a coefficient that never change between invoices but only depending on the product and stays static, please refer to the next chapter below about creating a custom tax per product.&lt;br /&gt;
&lt;br /&gt;
The preliminary and necessary step is to first create a custom field that will be used to modify the total price. We will create one for Client Invoice Lines.&lt;br /&gt;
&lt;br /&gt;
Go to the CustomFields administration panel, then into the Client Invoice Lines tab, then click on the button New Field, name the field &#039;&#039;&#039;coefficient&#039;&#039;&#039;, and set the type to &#039;&#039;&#039;Double&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
When you go to client invoices in Dolibarr, you should then see something similar to this:&lt;br /&gt;
[[File:Cfe1.jpg]]&lt;br /&gt;
You can see that our coefficient field was successfully created.&lt;br /&gt;
&lt;br /&gt;
Then here are two alternative ways of implementing the change of the total price of each product relativery to this coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change total price in database ==&lt;br /&gt;
&lt;br /&gt;
Here we will use a &amp;quot;save&amp;quot; overloading function to change the total price directly in the database, thus we won&#039;t have to modify anything else (eg: nor PDF templating nor ODT) since the total price will be already updated in the database.&lt;br /&gt;
&lt;br /&gt;
Warning: this method will work only with CustomFields =&amp;gt; 3.2.23 since the &amp;quot;save&amp;quot; overloading function did not work for products lines before.&lt;br /&gt;
&lt;br /&gt;
Just add the following overloading function in your customfields_fields_extend.lib.php file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_facturedet_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
 &lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $coefficient = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;coefficient&#039;}; // you could also use $coefficient = $value;&lt;br /&gt;
 &lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $coefficient = price2num($coefficient);&lt;br /&gt;
 &lt;br /&gt;
    // Get the original subprices (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
 &lt;br /&gt;
    // Update the subprices with the coefficient&lt;br /&gt;
    $object-&amp;gt;total_ht = $object-&amp;gt;total_ht * $coefficient; // compute no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva = $object-&amp;gt;total_tva * $coefficient; // compute tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc * $coefficient; // compute tax included price&lt;br /&gt;
 &lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(&#039;&#039;,true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
 &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;coefficient&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should show the correct total price on the Dolibarr&#039;s interface without modifying the unit subprice (P.U. Unit):&lt;br /&gt;
&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
You have nothing to edit in your PDF or ODT template, since the new subprice with applied coefficient will be automatically printed instead of the old one without coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change printed total price per product without tampering the subprice in the database ==&lt;br /&gt;
&lt;br /&gt;
Here is a way to change the printed total price (the one shown on screen and in PDF/ODT) but without changing the total price in the database.&lt;br /&gt;
&lt;br /&gt;
The following sub-chapters are all independent, thus you can only follow the ones that you require, eg: if you only need to change the price in your PDF documents, just create a custom field then jump to the sub-chapter to &#039;&#039;&#039;Implement the change of price in PDF&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in ODT templates ===&lt;br /&gt;
&lt;br /&gt;
There&#039;s no eval implementation currently inside ODT templates, but there is a kind of hook implementation, called [[Create_an_ODT_document_template#Other_personalized_tags|substitution functions]].&lt;br /&gt;
&lt;br /&gt;
You can either create your own module, then create your own substitution function, but for the sake of simplicity in this tutorial, we will directly use the CustomFields substitution function. However, please remember that you will have to redo your changes everytime you will update the CustomFields module, so if you don&#039;t want to do that it would be better if you create your own module and substitution function.&lt;br /&gt;
&lt;br /&gt;
Open the file &#039;&#039;&#039;htdocs/customfields/core/substitutions/functions_customfields.lib.php&#039;&#039;&#039; and go to the function &#039;&#039;&#039;customfields_completesubstitutionarray_lines&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
At the very end of this function, but inside the customfields check:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) {&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
But just before the ending bracket, you should add the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($object-&amp;gt;table_element_line === &#039;facturedet&#039;) { // First check that we are in the right module, we don&#039;t want to use coefficient for other modules if we didn&#039;t define the custom field there&lt;br /&gt;
    $substitutionarray[&#039;line_price_full&#039;] = $substitutionarray[&#039;line_price_ttc&#039;] * $substitutionarray[&#039;cf_coefficient&#039;]; // multiply the total price with our coefficient&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code will add a new tag &#039;&#039;&#039;{line_price_full}&#039;&#039;&#039; that you can then use inside your ODT templates.&lt;br /&gt;
&lt;br /&gt;
Just place this tag in a table in your ODT document, between the table tags ( [!-- BEGIN row.lines --] ... [!-- END row.lines --] ), and this will print your full price.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in PDF templates ===&lt;br /&gt;
&lt;br /&gt;
First, we will create our own PDF template by copying an existing template, and then we will edit it to change the price of items.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Creating your own PDF template by copying an existing template&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Go into the following folder: &#039;&#039;&#039;htdocs/core/modules/facture/doc/&#039;&#039;&#039;, then copy the file &#039;&#039;&#039;pdf_crabe.modules.php&#039;&#039;&#039; and name the new one &#039;&#039;&#039;pdf_crabecoeff.modules.php&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Then open it in your favourite editor, and change the following lines:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures // change pdf_crabe into pdf_crabecoeff&lt;br /&gt;
{&lt;br /&gt;
...&lt;br /&gt;
    function __construct($db)&lt;br /&gt;
    {&lt;br /&gt;
        ...&lt;br /&gt;
	$this-&amp;gt;name = &amp;quot;crabe&amp;quot;; // change &amp;quot;crabe&amp;quot; into &amp;quot;crabecoeff&amp;quot;&lt;br /&gt;
        ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you have your own PDF template!&lt;br /&gt;
&lt;br /&gt;
Now you should &#039;&#039;&#039;enable your PDF template&#039;&#039;&#039; before doing anything else, by going into the Modules Administration panel, and into the Invoice admin page, then click on the ON button at the right of the &#039;&#039;&#039;crabecoeff&#039;&#039;&#039; entry to enable the template.&lt;br /&gt;
&lt;br /&gt;
Now, let&#039;s proceed onto modifying the prices.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Changing the prices automatically in your PDF&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
You can now do pretty much anything you want in your PDF template, but I will give you a mean to automatically change the prices of every products with our coefficient, as well as the total price, without editing too much the template.&lt;br /&gt;
&lt;br /&gt;
First, try to find the following code of line (should be inside the write_file() method):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf=pdf_getInstance($this-&amp;gt;format);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, just copy/paste the following code &#039;&#039;&#039;below&#039;&#039;&#039; pdf_getInstance():&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$this-&amp;gt;customfields = customfields_fill_object($object, null, $outputlangs, null, true); // beautified values&lt;br /&gt;
$this-&amp;gt;customfields_raw = customfields_fill_object($object, null, $outputlangs, &#039;raw&#039;, null); // raw values&lt;br /&gt;
$this-&amp;gt;customfields_lines = customfields_fill_object_lines($object, null, $outputlangs, null, true); // product lines&#039; values&lt;br /&gt;
&lt;br /&gt;
// Resetting every $object price because we will compute them anew&lt;br /&gt;
$object-&amp;gt;total_ht = 0;&lt;br /&gt;
$object-&amp;gt;total_tva = 0;&lt;br /&gt;
$object-&amp;gt;total_ttc = 0;&lt;br /&gt;
// For each line of product/service, we will recompute all the prices&lt;br /&gt;
foreach($object-&amp;gt;lines as $line) {&lt;br /&gt;
    // Get the coefficient custom field&#039;s value for the current line&lt;br /&gt;
    $coeff = $object-&amp;gt;customfields-&amp;gt;lines-&amp;gt;{$line-&amp;gt;rowid}-&amp;gt;cf_coefficient;&lt;br /&gt;
    // Recompute the price of this product/service line&lt;br /&gt;
    $line-&amp;gt;total_ht = $line-&amp;gt;total_ht*$coeff; // compute no tax price&lt;br /&gt;
    $line-&amp;gt;total_tva = $line-&amp;gt;total_tva*$coeff; // compute tax amount&lt;br /&gt;
    $line-&amp;gt;total_ttc = $line-&amp;gt;total_ttc*$coeff; // compute tax included price&lt;br /&gt;
    // Do the sum of all new products/services prices and tva (will be incremented with every product/service line)&lt;br /&gt;
    $object-&amp;gt;total_ht += $line-&amp;gt;total_ht; // global no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva += $line-&amp;gt;total_tva; // global vat/tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc += $line-&amp;gt;total_ttc; // global  tax included price&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you should try to generate your PDF document, and you should get something like this:&lt;br /&gt;
[[File:Cfe3.jpg]]&lt;br /&gt;
&lt;br /&gt;
Note: printing the coefficient as a column in your PDF document is also possible but will not be covered here because PDF templates will vary for every persons depending on your needs.&lt;br /&gt;
For guidelines about how to do this, you can read the documentation on [[Create_a_PDF_document_template|how to create a PDF template]] and [[Module_CustomFields#Implementing_in_PDF_templates|how to use custom fields in PDF templates]].&lt;br /&gt;
&lt;br /&gt;
Hint: you should be looking inside the following conditional block, inside the write_file() method:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
{&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of total price in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
Unluckily there is not (yet) any hooking context to modify how lines of products/services are printed, thus the only thing you can do is directly edit the &#039;&#039;&#039;template file&#039;&#039;&#039; and manually put inside your code to load your custom field and print it the way you want using HTML formatting, for our example:&lt;br /&gt;
&lt;br /&gt;
* For Dolibarr 3.3 and above, edit the file &#039;&#039;&#039;htdocs/core/tpl/objectline_view.tpl.php&#039;&#039;&#039;&lt;br /&gt;
* For Dolibarr 3.1 and 3.2, edit the file &#039;&#039;&#039;htdocs/core/tpl/freeproductline_view.tpl.php&#039;&#039;&#039; AND &#039;&#039;&#039;predefinedproductline_view.tpl.php&#039;&#039;&#039; (apply the same changes to both files)&lt;br /&gt;
&lt;br /&gt;
In the template file you opened, find the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And replace it by:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $object-&amp;gt;table_element_line; // you can use $object-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
&lt;br /&gt;
// Modifying the HT price and printing it on the Dolibarr&#039;s datasheet interface&lt;br /&gt;
// Applying a multiplying/dividing coefficient to an HT price will give a mathematically correct TTC price since the taxes are also a multiplier&lt;br /&gt;
// However, if you want to apply other mathematical operations (like addition or substaction), you should use the total price with $line-&amp;gt;total_ttc&lt;br /&gt;
if (!empty($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
    echo price($line-&amp;gt;total_ht * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient);&lt;br /&gt;
} else {&lt;br /&gt;
    echo price($line-&amp;gt;total_ht);&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should produce something like this:&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Technical note: here we are using customfields_fill_object() instead of customfields_fill_object_lines() (the latter being specifically adapted to lines and thus should theoretically be used here) for performance reasons because the template file we are editing is called in a loop, and customfields_fill_object_lines() should be called outside a loop (because it already does the job that is done by the loop), so here we &#039;tricks&#039; the customfields_fill_object() function to avoid this loop by submitting &#039;facturedet&#039; as a module by itself (when in fact it&#039;s a product lines table, but it works the same way nevertheless). This is a good demonstration of the flexibility of the CustomFields functions and methods.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the coefficient column in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
It&#039;s also possible to show the coefficient in a new column on the Dolibarr interface.&lt;br /&gt;
&lt;br /&gt;
To do that, you need to do two things:&lt;br /&gt;
&lt;br /&gt;
1- Add the column title &amp;quot;Coeff.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
2- Add the generator that will input the coefficient value for each line.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Add the column title &amp;quot;Coeff.&amp;quot;&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
Open in your favourite editor the file /htdocs/core/class/commonobject.class.php and inside the function printObjectLines (note the plural s), find the following lines of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Total HT&lt;br /&gt;
print &#039;&amp;lt;td align=&amp;quot;right&amp;quot; width=&amp;quot;50&amp;quot;&amp;gt;&#039;.$langs-&amp;gt;trans(&#039;TotalHTShort&#039;).&#039;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is what prints the Total HT price column. We will place the Coeff column just before.&lt;br /&gt;
&lt;br /&gt;
So, just above this code, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
dol_include_once(&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element_line);&lt;br /&gt;
$coeff_field = $customfields-&amp;gt;fetchFieldStruct(&#039;coefficient&#039;);&lt;br /&gt;
if (!empty($coeff_field)) {&lt;br /&gt;
    print &#039;&amp;lt;td align=&amp;quot;right&amp;quot; width=&amp;quot;50&amp;quot;&amp;gt;&#039;.$langs-&amp;gt;trans(&#039;Coeff.&#039;).&#039;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will take care of showing the Coeff. column, but only for modules where you created such a custom field (because the file we modified is a common object for all modules in Dolibarr, not just invoices, thus this modification will gracefully skip this custom field if it does not exists for other modules).&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Add the generator&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/tpl/objectline_view.tpl.php and try to find the following block of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php if ($line-&amp;gt;special_code == 3)	{ ?&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo $langs-&amp;gt;trans(&#039;Option&#039;); ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } else { ?&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is what prints the total HT price value for each line.&lt;br /&gt;
&lt;br /&gt;
Just above of this block of code, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $object-&amp;gt;table_element_line; // you can use $this-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
if (isset($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
    &amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient; ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just like for the column title, this code will take care of generating the coefficient value only if the custom field is defined, because this file is also a generic template for all modules of Dolibarr.&lt;br /&gt;
&lt;br /&gt;
At this point, everything is done, you should see the following:&lt;br /&gt;
[[File:Cf_wiki_coeff_column.png]]&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in margin module ===&lt;br /&gt;
&lt;br /&gt;
If you enabled the margin module, you should see that the coefficient is not applied there, but it&#039;s easy to fix that.&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/class/commonobject.class.php and find the function getMarginInfos(), then inside find the following lines:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach($this-&amp;gt;lines as $line) {&lt;br /&gt;
    if (empty($line-&amp;gt;pa_ht) &amp;amp;&amp;amp; isset($line-&amp;gt;fk_fournprice) &amp;amp;&amp;amp; !$force_price) {&lt;br /&gt;
        $product = new ProductFournisseur($this-&amp;gt;db);&lt;br /&gt;
        if ($product-&amp;gt;fetch_product_fournisseur_price($line-&amp;gt;fk_fournprice))&lt;br /&gt;
            $line-&amp;gt;pa_ht = $product-&amp;gt;fourn_unitprice * (1 - $product-&amp;gt;fourn_remise_percent / 100);&lt;br /&gt;
        if (isset($conf-&amp;gt;global-&amp;gt;MARGIN_TYPE) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;MARGIN_TYPE == &amp;quot;2&amp;quot; &amp;amp;&amp;amp; $product-&amp;gt;fourn_unitcharges &amp;gt; 0)&lt;br /&gt;
            $line-&amp;gt;pa_ht += $product-&amp;gt;fourn_unitcharges;&lt;br /&gt;
    }&lt;br /&gt;
    // si prix d&#039;achat non renseigné et devrait l&#039;être, alors prix achat = prix vente&lt;br /&gt;
    if ((!isset($line-&amp;gt;pa_ht) || $line-&amp;gt;pa_ht == 0) &amp;amp;&amp;amp; $line-&amp;gt;subprice &amp;gt; 0 &amp;amp;&amp;amp; (isset($conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull == 1)) {&lt;br /&gt;
        $line-&amp;gt;pa_ht = $line-&amp;gt;subprice * (1 - ($line-&amp;gt;remise_percent / 100));&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the lines are filled with margin infos. The price has just been fetched here, and we will now modify it with the coefficient before the price gets used for margin computation.&lt;br /&gt;
&lt;br /&gt;
Just below the code we found, paste the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $line-&amp;gt;table_element;&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
if (!empty($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
    $line-&amp;gt;pa_ht = $line-&amp;gt;pa_ht * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient;&lt;br /&gt;
    $line-&amp;gt;subprice = $line-&amp;gt;subprice * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s all, the price should be reflected in the margin.&lt;br /&gt;
[[File:Cf_wiki_coeff_margin.png]]&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in remaining debt to pay ===&lt;br /&gt;
&lt;br /&gt;
Open /htdocs/core/class/commonobject.class.php and look for the function update_price().&lt;br /&gt;
&lt;br /&gt;
You should see the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function update_price($exclspec=0,$roundingadjust=&#039;none&#039;,$nodatabaseupdate=0,$seller=&#039;&#039;)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just below the include_once line, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;;&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/conf/conf_customfields_func.lib.php&#039;;&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $this-&amp;gt;table_element_line);&lt;br /&gt;
// Fetching line ids from parent object id&lt;br /&gt;
$line_ids = $customfields-&amp;gt;fetchAny(&#039;rowid&#039;, MAIN_DB_PREFIX.$this-&amp;gt;table_element_line, $this-&amp;gt;fk_element.&#039; = &#039;.$this-&amp;gt;id);&lt;br /&gt;
$line_ids = array_values_recursive(&#039;rowid&#039;, $line_ids);&lt;br /&gt;
// Load custom fields&lt;br /&gt;
$cf_records = $customfields-&amp;gt;fetch($line_ids);&lt;br /&gt;
// Reassociate the records with the line id (instead of the customfields record id)&lt;br /&gt;
$cf_line_cfrowid_to_rowid = array_reassociate(&#039;rowid&#039;, &#039;fk_&#039;.$this-&amp;gt;table_element_line, $cf_records);&lt;br /&gt;
$tmp = $cf_records;&lt;br /&gt;
$cf_records = array();&lt;br /&gt;
foreach ($tmp as $k=&amp;gt;$v) { $cf_records[$cf_line_cfrowid_to_rowid[$k]] = $tmp[$k]; }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will load the coefficient custom field (and also any other custom field you have defined for lines).&lt;br /&gt;
&lt;br /&gt;
Now scroll below, and you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
while ($i &amp;lt; $num)&lt;br /&gt;
{&lt;br /&gt;
    $obj = $this-&amp;gt;db-&amp;gt;fetch_object($resql);&lt;br /&gt;
&lt;br /&gt;
    // Note: There is no check on detail line and no check on total, if $forcedroundingmode = &#039;none&#039;&lt;br /&gt;
&lt;br /&gt;
    if ($forcedroundingmode == &#039;0&#039;)	// Check if data on line are consistent. This may solve lines that were not consistent because set with $forcedroundingmode=&#039;auto&#039;&lt;br /&gt;
    {&lt;br /&gt;
        $localtax_array=array($obj-&amp;gt;localtax1_type,$obj-&amp;gt;localtax1_tx,$obj-&amp;gt;localtax2_type,$obj-&amp;gt;localtax2_tx);&lt;br /&gt;
        $tmpcal=calcul_price_total($obj-&amp;gt;qty, $obj-&amp;gt;up, $obj-&amp;gt;remise_percent, $obj-&amp;gt;vatrate, $obj-&amp;gt;localtax1_tx, $obj-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $obj-&amp;gt;info_bits, $obj-&amp;gt;product_type, $seller, $localtax_array);&lt;br /&gt;
        $diff=price2num($tmpcal[1] - $obj-&amp;gt;total_tva, &#039;MT&#039;, 1);&lt;br /&gt;
        if ($diff)&lt;br /&gt;
        {&lt;br /&gt;
            $sqlfix=&amp;quot;UPDATE &amp;quot;.MAIN_DB_PREFIX.$this-&amp;gt;table_element_line.&amp;quot; SET &amp;quot;.$fieldtva.&amp;quot; = &amp;quot;.$tmpcal[1].&amp;quot;, total_ttc = &amp;quot;.$tmpcal[2].&amp;quot; WHERE rowid = &amp;quot;.$obj-&amp;gt;rowid;&lt;br /&gt;
            dol_syslog(&#039;We found unconsistent data into detailed line (difference of &#039;.$diff.&#039;) for line rowid = &#039;.$obj-&amp;gt;rowid.&amp;quot; (total vat of line calculated=&amp;quot;.$tmpcal[1].&amp;quot;, database=&amp;quot;.$obj-&amp;gt;total_tva.&amp;quot;). We fix the total_vat and total_ttc of line by running sqlfix = &amp;quot;.$sqlfix);&lt;br /&gt;
            $resqlfix=$this-&amp;gt;db-&amp;gt;query($sqlfix);&lt;br /&gt;
            if (! $resqlfix) dol_print_error($this-&amp;gt;db,&#039;Failed to update line&#039;);&lt;br /&gt;
            $obj-&amp;gt;total_tva = $tmpcal[1];&lt;br /&gt;
            $obj-&amp;gt;total_ttc = $tmpcal[2];&lt;br /&gt;
            //&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just after, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
$cf_coeff = $cf_records[$obj-&amp;gt;rowid]-&amp;gt;coefficient;&lt;br /&gt;
if (empty($cf_coeff) and $cf_coeff !== 0) $cf_coeff = 1; // by default, an empty coefficient = 1&lt;br /&gt;
$obj-&amp;gt;total_ht = $obj-&amp;gt;total_ht * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_tva = $obj-&amp;gt;total_tva * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_localtax1 = $obj-&amp;gt;total_localtax1 * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_localtax2 = $obj-&amp;gt;total_localtax2 * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_ttc = $obj-&amp;gt;total_ttc * $cf_coeff;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should do the trick. Now go to one of your invoices (or whatever module you are targetting), and try to add/edit a product/service line to force refresh the total price. You should then see this:&lt;br /&gt;
[[File:Cf_wiki_coeff_remaining_debt.png]]&lt;br /&gt;
&lt;br /&gt;
= Custom tax per product =&lt;br /&gt;
&lt;br /&gt;
This part will show you how to create a static custom tax per product. Static means that the tax will be stored per product, and not per product&#039;s line, thus you just have to setup the tax once per product and it will always be applied in every invoice you add this product.&lt;br /&gt;
&lt;br /&gt;
To do that, you will need the CustomFields Pro module + ProductsEasyAccess.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
1- Create a product&#039;s custom fields called &amp;quot;custom_tax&amp;quot;. Now, using ProductsEasyAccess, you can access this field wherever you wants, either to show it on the Dolibarr&#039;s interface (see previous chapter), or in a PDF template, which we will describe below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2- Modify your invoice&#039;s PDF template. You first need to load all the products&#039; fields &#039;&#039;before&#039;&#039; the loop over the products&#039; lines. To do this, search for a line like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// This should be placed above the &amp;quot;loop lines&amp;quot; below, add this:&lt;br /&gt;
&lt;br /&gt;
dol_include_once(&#039;/productseasyaccess/lib/productseasyaccess.lib.php&#039;);&lt;br /&gt;
fill_object_with_products_fields($object);&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines, search for this command&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  // Add this just below the for loop, this will ease your php manipulation after.&lt;br /&gt;
  // This will allow you to more easily access the products&#039; fields (this will store the id of each product inside $lineproductid).&lt;br /&gt;
  $line_product_id = $object-&amp;gt;lines[$i]-&amp;gt;fk_product;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4- You can now use your products&#039; fields in your PDF. To access the custom_tax field, just use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And to print it in your PDF:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell(0,3, $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax, 0, &#039;L&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5- Then, if you want to count the custom tax total and add it to your vat total, you can create a counter inside the for loop we cited in the first step:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_custom_tax = 0; // Initialize the custom tax total counter&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  $object-&amp;gt;total_custom_tax += $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then find inside the function **_tableau_tot**, find the following line:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And prepend the total modification like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc + $object-&amp;gt;total_custom_tax; // prepend this to add the custom tax to the total&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Done. This should show your custom tax per product and your total including all custom taxes.&lt;/div&gt;</summary>
		<author><name>Lrq3000</name></author>
	</entry>
	<entry>
		<id>https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33181</id>
		<title>Module CustomFields Cases</title>
		<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33181"/>
		<updated>2015-02-24T16:02:30Z</updated>

		<summary type="html">&lt;p&gt;Lrq3000: /* Dynamic condition */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:CustomFields]]&lt;br /&gt;
{{TemplateDocDevEn}}&lt;br /&gt;
{{TemplateModEN}}&lt;br /&gt;
{{BasculeDevUserEn|&lt;br /&gt;
name=CustomFields|&lt;br /&gt;
num=8500|&lt;br /&gt;
devdoc=[[Module_CustomFields_Dev]]|&lt;br /&gt;
userdoc=[[Module_CustomFields]]|}}&lt;br /&gt;
This page lists a compilation of concrete, practical examples uses of [[Module_CustomFields|CustomFields]] and of its various features.&lt;br /&gt;
&lt;br /&gt;
If you had an experience with CustomFields, you can also add here your case as an example for others to get inspired.&lt;br /&gt;
&lt;br /&gt;
= How to make a generic PDF template listing all custom fields =&lt;br /&gt;
&lt;br /&gt;
To make a generic PDF template listing all custom fields, you just need to take an existing PDF template, then you need to follow these 3 steps:&lt;br /&gt;
&lt;br /&gt;
1/ Edit the class name (required, else it will conflict with the original template).&lt;br /&gt;
&lt;br /&gt;
Eg: if we take the crabe template for invoices, we change:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;crabe&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
into:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_customfields extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;customfields&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2/ Copy this generic function at the end of the PDF:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propale/order/etc... (CustomFields simpler functions will automatically adapt)&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs); // set default PDF font size&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
    // Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
    $customfields = customfields_fill_object($object, null, $outputlangs, null, true);&lt;br /&gt;
&lt;br /&gt;
    // Setting the starting position of the text cursor&lt;br /&gt;
    $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
    $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
    // Printing the customfields&lt;br /&gt;
    foreach ($object-&amp;gt;customfields as $label=&amp;gt;$value) { // $value is already formatted!&lt;br /&gt;
        // Get translated label&lt;br /&gt;
        $translatedlabel = $customfields-&amp;gt;findLabelPDF($label, $outputlangs); // translated label of the customfield (not translated by default in customfields_fill_object() because a field should always be accessible by a base name, whatever the translation is)&lt;br /&gt;
&lt;br /&gt;
        // PDF formatting, placement and printing&lt;br /&gt;
        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
        $pdf-&amp;gt;MultiCell(0,3, $translatedlabel.&#039; ($object-&amp;gt;customfields-&amp;gt;&#039;.$label.&#039;): &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternative function (with a bit less functionalities, but more controllable):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page (used to debug if CustomFields setup is correct)&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propal/product/whatever...&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs);&lt;br /&gt;
&lt;br /&gt;
            if (empty($object-&amp;gt;table_element) or empty($object-&amp;gt;id)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;Current \$object is not compatible with CustomFields, could not find table_element or id.&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
    $customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element);&lt;br /&gt;
&lt;br /&gt;
            // Fetching custom fields records&lt;br /&gt;
            $fields = $customfields-&amp;gt;fetch($object-&amp;gt;id);&lt;br /&gt;
&lt;br /&gt;
            if (!isset($fields)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;No custom field could be found for this object. Please check your configuration (did you create at least one customfield and set a value in the current datasheet?)&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            } else {&lt;br /&gt;
                // Setting the starting position of the text cursor&lt;br /&gt;
                $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
                $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
                // Printing the customfields&lt;br /&gt;
                foreach ($fields as $key=&amp;gt;$field) {&lt;br /&gt;
                        $translatedname = $customfields-&amp;gt;findLabelPDF($key, $outputlangs); // label of the customfield&lt;br /&gt;
                        $value = $customfields-&amp;gt;simpleprintFieldPDF($key, $field, $outputlangs); // value (cleaned and properly formatted) of the customfield&lt;br /&gt;
&lt;br /&gt;
                        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
                        $pdf-&amp;gt;MultiCell(0,3, $translatedname.&#039;: &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
                        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3/ Call the _pagecustomfields function at the right place (generally, after _pagefoot and just before $pdf-&amp;gt;Close()):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Pied de page&lt;br /&gt;
$this-&amp;gt;_pagefoot($pdf,$object,$outputlangs);&lt;br /&gt;
$pdf-&amp;gt;AliasNbPages();&lt;br /&gt;
&lt;br /&gt;
// CustomFields&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) { // if the customfields module is activated...&lt;br /&gt;
        // Add a page with the customfields&lt;br /&gt;
        $pdf-&amp;gt;AddPage();&lt;br /&gt;
        $this-&amp;gt;_pagecustomfields($pdf,$object,$outputlangs);&lt;br /&gt;
        $pagenb++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
$pdf-&amp;gt;Close(); // you should first search for this, you should only find one occurrence&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then go to the module&#039;s admin panel (eg: invoice admin panel) and enable the template you&#039;ve just created.&lt;br /&gt;
&lt;br /&gt;
You can now use it to test if your customfields work.&lt;br /&gt;
&lt;br /&gt;
= Conditions on fields selection =&lt;br /&gt;
&lt;br /&gt;
Sometimes you may want to show a DropdownBox or Constraint (or another multi-choice type), but you may want to show or hide choices based on conditions (eg: only if it&#039;s a prospect, only if this zone was selected, only if this category was selected, etc..).&lt;br /&gt;
&lt;br /&gt;
To know which is the best solution you should choose to implement your condition, you should first know in which category your condition falls in.&lt;br /&gt;
&lt;br /&gt;
== Conditions categories ==&lt;br /&gt;
&lt;br /&gt;
Conditions can be categorized into 4 types:&lt;br /&gt;
&lt;br /&gt;
* static condition: choices are restrained on a condition that never changes (eg: only show third-parties that are suppliers, or lowercase all data on sql insertion, or check that a field is above a certain number, etc.).&lt;br /&gt;
* semi-dynamic condition: choices that are constrained on a static table but based on the value of another field/customfield. This is also commonly called &amp;quot;Cascaded dropdown lists&amp;quot; or &amp;quot;Dynamically linked dropdown lists&amp;quot;.&lt;br /&gt;
* dynamic: everything is dynamic: choices are based on a subfield of a field of the current module, so that there&#039;s no material table and it&#039;s impossible to create a view (because you would need to create one view per id) (eg: show only the contacts attached to the third-party being accessed)&lt;br /&gt;
* timed condition: a condition that is based on time or recurrence (eg: at 5:00 AM delete old records, etc..)&lt;br /&gt;
&lt;br /&gt;
== Conditions solutions ==&lt;br /&gt;
&lt;br /&gt;
Now that you know what kind of condition you want to set, you can choose one of the following solutions depending on your preferences and knowledge.&lt;br /&gt;
&lt;br /&gt;
In any case, you can always use the &#039;&#039;&#039;CustomFields&#039;s overload functions&#039;&#039;&#039; to fit your needs for nearly every type of condition.&lt;br /&gt;
&lt;br /&gt;
=== Static condition ===&lt;br /&gt;
&lt;br /&gt;
* View: create a view on the table based on the condition, and then create a custom field constrained on this view (which is just like any table), eg: only show third-parties that are suppliers:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
CREATE VIEW llx_societe_supplier_view AS&lt;br /&gt;
SELECT *&lt;br /&gt;
FROM llx_societe&lt;br /&gt;
WHERE fournisseur=1;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
WARNING: foreign keys (and thus constraints) can&#039;t be used on views with MySQL, and materialized views neither exist, so you can&#039;t use this method with MySQL.&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
* SQL Transformations&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
=== Semi-dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade option is ideal in most cases, and if not enough, you can use the Cascade Custom option and Overloading Functions.&lt;br /&gt;
* View&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
&lt;br /&gt;
=== Dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade Custom and Overload functions&lt;br /&gt;
* Recursive Remote Fields Access&lt;br /&gt;
* Making your own module, which calls the CustomFields class&lt;br /&gt;
&lt;br /&gt;
=== Timed condition ===&lt;br /&gt;
&lt;br /&gt;
* SQL scheduled events&lt;br /&gt;
* Cron job&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
= Overloading functions =&lt;br /&gt;
&lt;br /&gt;
Here are a few concrete use cases of overloading functions to achieve various goals.&lt;br /&gt;
&lt;br /&gt;
== Dynamic list selection based on another one ==&lt;br /&gt;
&lt;br /&gt;
The goal is to show two custom fields on the Third-Party module: one which gives the zone (secteur) of the third-party (Isere, Alpes du sud, Haute-Savoie...) and the other which gives relative to the zone the list of all the ski resorts (station_a) inside the selected zone.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will show how to manually manage this kind of requirements using [[Module_CustomFields#Overloading_functions:_Adding_your_own_management_code_for_custom_fields|overloading functions]], but in latest CustomFields releases, the same solution can be achieved automatically using the [[Module_CustomFields#Cascade|Cascade option]].&lt;br /&gt;
&lt;br /&gt;
We have one table &#039;&#039;&#039;llx_k_station&#039;&#039;&#039; (rowid, station (char50), secteur(char50)) which contains the following: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1,les 2 alpes,isère&lt;br /&gt;
2,chamrousse,isère&lt;br /&gt;
3,alpe d&#039;huez,isère&lt;br /&gt;
4,vars,alpes du sud&lt;br /&gt;
5,risoul,alpes du sud&lt;br /&gt;
etc...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In CustomFields&#039;s admin panel, we create two custom fields:&lt;br /&gt;
&lt;br /&gt;
1- secteur: returns the list of all zones - type DropdownBox: enum(&#039;- Aucun&#039;,&#039;Alpes du Sud&#039;,&#039;export&#039;,&#039;Haute-Savoie&#039;,&#039;Isère&#039;, etc...)&lt;br /&gt;
&lt;br /&gt;
2- station_a: returns the list of all ski resorts - type constraint on &#039;&#039;&#039;llx_k_station&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Here is the code to put in customfields_fields_extend.lib.php that will allow to show only the ski resorts corresponding to the selected zone:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_editview_societe_station_a (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$parameters, &amp;amp;$action, &amp;amp;$user, &amp;amp;$idvar, &amp;amp;$rightok, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
  global $db; // allow to access database functions&lt;br /&gt;
  $sql=&amp;quot;SELECT llx_k_station.station, llx_k_station.rowid FROM llx_societe_customfields INNER JOIN llx_k_station ON llx_societe_customfields.Secteur = llx_k_station.secteur WHERE llx_societe_customfields.fk_societe=&amp;quot;.$object-&amp;gt;id;&lt;br /&gt;
$result=$db-&amp;gt;query($sql);&lt;br /&gt;
 &lt;br /&gt;
  if ($result) {&lt;br /&gt;
    $num = $db-&amp;gt;num_rows($result);&lt;br /&gt;
    $i = 0;&lt;br /&gt;
    $myarray = array();&lt;br /&gt;
    while ($i &amp;lt; $num)&lt;br /&gt;
    {&lt;br /&gt;
      $obj = $db-&amp;gt;fetch_object($result);&lt;br /&gt;
      $myarray []=array(&#039;id&#039;=&amp;gt;$obj-&amp;gt;rowid, &#039;value&#039;=&amp;gt;$obj-&amp;gt;station);&lt;br /&gt;
      $i++;&lt;br /&gt;
    }&lt;br /&gt;
    $value = $myarray; &lt;br /&gt;
    $db-&amp;gt;free($result);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to manub for giving this case.&lt;br /&gt;
&lt;br /&gt;
== Dynamic discount computation per products lines on propales ==&lt;br /&gt;
&lt;br /&gt;
The goal here is to compute the discount for a product line in propales based on a computation on three custom fields.&lt;br /&gt;
&lt;br /&gt;
First, create three custom fields for &amp;quot;Line commercial proposals&amp;quot;: vl, cpl and lc.&lt;br /&gt;
&lt;br /&gt;
Then add the following overloading function at the end of your customfields_fields_extend.lib.php file (read the comments for more informations):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_propaldet_vl (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
&lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $vl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;vl&#039;};&lt;br /&gt;
    $cpl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;cpl&#039;};&lt;br /&gt;
    $lc = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;lc&#039;};&lt;br /&gt;
&lt;br /&gt;
    // Compute the discount&lt;br /&gt;
    $remise_calculee = (100 - $vl) * (100 - $cpl) * (100 - $lc);&lt;br /&gt;
&lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $remise_calculee = price2num($remise_calculee);&lt;br /&gt;
&lt;br /&gt;
    // Assign the new discount into the PropalLine object&lt;br /&gt;
    $object-&amp;gt;remise_percent = $remise_calculee;&lt;br /&gt;
&lt;br /&gt;
    // Update the price (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
&lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
&lt;br /&gt;
    //$q = &amp;quot;UPDATE  &amp;quot;.MAIN_DB_PREFIX.&amp;quot;propaldet SET remise_percent=&amp;quot;.$remise_calculee.&amp;quot; WHERE  rowid=&amp;quot;.$object-&amp;gt;rowid;&lt;br /&gt;
    //$db-&amp;gt;query($q);&lt;br /&gt;
    &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;vl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;cpl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;lc&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Linking Dolibarr objects from two different modules =&lt;br /&gt;
&lt;br /&gt;
Dolibarr sometimes offers the possibility to link two different modules. Eg: when you convert an Intervention card into an Invoice.&lt;br /&gt;
&lt;br /&gt;
In this case, you might want in your templates to fetch the custom fields of the linked object. Eg: you generate a PDF from the Invoice, but you want to get the custom fields from the Intervention that was converted into the Invoice.&lt;br /&gt;
&lt;br /&gt;
The key here is to use the &#039;&#039;&#039;llx_element_element&#039;&#039;&#039; table or the &#039;&#039;&#039;llx_element_*&#039;&#039;&#039; tables (eg: llx_element_contact): this is a standard Dolibarr table that stores all links between objects of two different modules.&lt;br /&gt;
&lt;br /&gt;
Here is the structure:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
rowid | fk_source | sourcetype | fk_target | targettype&lt;br /&gt;
-------------------------------------------------------&lt;br /&gt;
1 | 2 | fichinter | 5 | facture&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can then use a php code like the following to populate your $object with customfields:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
// This will also create and returns us an instance of the CustomFields class, which we will use to manually query the llx_element_element table&lt;br /&gt;
$customfields = customfields_fill_object($object, null, $outputlangs, &#039;facture&#039;, true); // beautified values&lt;br /&gt;
&lt;br /&gt;
// Querying the llx_element_element table manually to get the link between the invoice and the linked intervention card (we want the id of the intervention card)&lt;br /&gt;
// fetchAny($columns, $table, $where=&#039;&#039;, $orderby=&#039;&#039;, $limitby=&#039;&#039;)&lt;br /&gt;
$eltrow = $customfields-&amp;gt;fetchAny(&#039;*&#039;, MAIN_DB_PREFIX.&#039;element_element&#039;, &#039;targettype=&amp;quot;&#039;.$object-&amp;gt;element.&#039;&amp;quot; and fk_target=&#039;.$object-&amp;gt;id.&#039; and sourcetype=&amp;quot;fichinter&amp;quot;&#039;);&lt;br /&gt;
&lt;br /&gt;
// Save the intervention&#039;s id&lt;br /&gt;
$sourceid = $eltrow[0][&#039;fk_source&#039;];&lt;br /&gt;
&lt;br /&gt;
// Crafting a dummy intervention object&lt;br /&gt;
$fromobject = new stdClass(); // Init an empty object&lt;br /&gt;
$fromobject-&amp;gt;id = $sourceid; // We need the id and..&lt;br /&gt;
$fromobject-&amp;gt;table_element = &#039;fichinter&#039;; // the table_element (name of the table)&lt;br /&gt;
&lt;br /&gt;
// We can then fill $object with the intervention custom fields (we store them in a sub property &#039;fichinter&#039; to avoid conflicts with invoice&#039;s custom fields)&lt;br /&gt;
customfields_fill_object($object, $fromobject, $outputlangs, &#039;fichinter&#039;, true);&lt;br /&gt;
&lt;br /&gt;
// Now you can access invoices custom fields using:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;facture-&amp;gt;cf_myfield);&lt;br /&gt;
// And access intervention custom fields with:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;fichinter-&amp;gt;cf_myfield);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to netassopro for the tip.&lt;br /&gt;
&lt;br /&gt;
= Modify the total global price with a coefficient (without tampering the discount) =&lt;br /&gt;
&lt;br /&gt;
This is more troublesome than modifying the total price per product/service line, because the total global price of an object (let&#039;s say an invoice, but this also works for any other object) is always recomputed by summing the prices and quantity of each product/service line. This means that although there&#039;s an entry &amp;quot;total_ttc&amp;quot; in the database to store the invoice&#039;s total price, this price is in fact always recomputed, it&#039;s never used as-is. Thus, it&#039;s useless to change only the database record: we need to inject some code to change how the total price is computed.&lt;br /&gt;
&lt;br /&gt;
This could be done using an overloading function or a trigger, but unluckily, Dolibarr doesn&#039;t call a trigger nor a hook when recomputing the price. We thus need to edit core files to do what we want.&lt;br /&gt;
&lt;br /&gt;
However, the bright side is that we can cleverly edit just two functions from one core file: update_price() and getMarginInfos() from /htdocs/core/class/commonobject.class.php , and everything should run smoothly. Beware that if you update Dolibarr, you will need to redo these two modifications in the code.&lt;br /&gt;
&lt;br /&gt;
First, you need to create a new custom field called &amp;quot;global_coefficient&amp;quot; in Invoices (not Invoices Lines!). Then you can continue to the subchapters below.&lt;br /&gt;
&lt;br /&gt;
Also note that this global coefficient is also compatible with per line coefficients (as described in the next chapter), thus you can use a global coefficient and per line coefficients at the same time. You can also use multiple global coefficient if you wish.&lt;br /&gt;
&lt;br /&gt;
Another note: if you have a grid of discounts depending on quantity or price or whatever parameter, you can program a precomputation code using a Custom Ajax function (this way, your discounts will be automatically computed, but then you can still manually enter them for particular cases since the custom field is editable).&lt;br /&gt;
&lt;br /&gt;
== Implement the change of global price in most of Dolibarr (including remaining debt to pay and PDF and ODT) ==&lt;br /&gt;
&lt;br /&gt;
The change we will implement here will reflect the new total price with the global coefficient in most of Dolibarr&#039;s code, since we will here change the main function that is usually called to update and get the total price.&lt;br /&gt;
&lt;br /&gt;
Open /htdocs/core/class/commonobject.class.php and look for the function update_price().&lt;br /&gt;
&lt;br /&gt;
You should see the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function update_price($exclspec=0,$roundingadjust=&#039;none&#039;,$nodatabaseupdate=0,$seller=&#039;&#039;)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just below the include_once line, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;;&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/conf/conf_customfields_func.lib.php&#039;;&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $this-&amp;gt;table_element);&lt;br /&gt;
// Get object&#039;s id (either stored in property &amp;quot;id&amp;quot; or &amp;quot;rowid&amp;quot;)&lt;br /&gt;
$this_id = (!empty($this-&amp;gt;id)) ? $this-&amp;gt;id : $this-&amp;gt;rowid;&lt;br /&gt;
// Load custom fields records&lt;br /&gt;
$cf_parent_record = $customfields-&amp;gt;fetch($this_id);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will load the global_coefficient custom field (and also any other custom field you have defined for invoices).&lt;br /&gt;
&lt;br /&gt;
Now scroll below, and you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
while ($i &amp;lt; $num)&lt;br /&gt;
{&lt;br /&gt;
    $obj = $this-&amp;gt;db-&amp;gt;fetch_object($resql);&lt;br /&gt;
&lt;br /&gt;
    ... (lots of code)&lt;br /&gt;
&lt;br /&gt;
    i++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Add revenue stamp to total&lt;br /&gt;
$this-&amp;gt;total_ttc       += isset($this-&amp;gt;revenuestamp)?$this-&amp;gt;revenuestamp:0;&lt;br /&gt;
&lt;br /&gt;
$this-&amp;gt;db-&amp;gt;free($resql);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is the end of the loop that is summing each product/service line price to compute the total price. Just after this loop, you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Now update global field total_ht, total_ttc and tva&lt;br /&gt;
$fieldht=&#039;total_ht&#039;;&lt;br /&gt;
$fieldtva=&#039;tva&#039;;&lt;br /&gt;
$fieldlocaltax1=&#039;localtax1&#039;;&lt;br /&gt;
$fieldlocaltax2=&#039;localtax2&#039;;&lt;br /&gt;
$fieldttc=&#039;total_ttc&#039;;&lt;br /&gt;
// Specific code for backward compatibility with old field names&lt;br /&gt;
if ($this-&amp;gt;element == &#039;facture&#039; || $this-&amp;gt;element == &#039;facturerec&#039;)             $fieldht=&#039;total&#039;;&lt;br /&gt;
if ($this-&amp;gt;element == &#039;facture_fourn&#039; || $this-&amp;gt;element == &#039;invoice_supplier&#039;) $fieldtva=&#039;total_tva&#039;;&lt;br /&gt;
if ($this-&amp;gt;element == &#039;propal&#039;)                                                $fieldttc=&#039;total&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Between those two blocks of code (so just above // Now update global field total_ht, total_ttc and tva), you can place the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
if (!empty($cf_parent_record-&amp;gt;global_coefficient)) { // avoid applying the coefficient if empty (this will return a 0 price)&lt;br /&gt;
    $this-&amp;gt;total_ht = $this-&amp;gt;total_ht * $cf_parent_record-&amp;gt;global_coefficient;&lt;br /&gt;
    $this-&amp;gt;total_tva = $this-&amp;gt;total_tva * $cf_parent_record-&amp;gt;global_coefficient;&lt;br /&gt;
    $this-&amp;gt;total_ttc = $this-&amp;gt;total_ttc * $cf_parent_record-&amp;gt;global_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Done, now try to add/edit a product/service line, and the total price should be affected by the global coefficient.&lt;br /&gt;
&lt;br /&gt;
Note that this modification &#039;&#039;&#039;needs only to be done once for all modules&#039;&#039;&#039;, you then just have to create a custom field named &amp;quot;global_coefficient&amp;quot; in every module you want to apply this coefficient.&lt;br /&gt;
&lt;br /&gt;
== Implementing the change of price in margin module ==&lt;br /&gt;
&lt;br /&gt;
If you enabled the margin module, you should see that the coefficient is not applied there, but it&#039;s easy to fix that.&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/class/commonobject.class.php and find the function getMarginInfos(), then inside find the following lines:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach($this-&amp;gt;lines as $line) {&lt;br /&gt;
    if (empty($line-&amp;gt;pa_ht) &amp;amp;&amp;amp; isset($line-&amp;gt;fk_fournprice) &amp;amp;&amp;amp; !$force_price) {&lt;br /&gt;
        $product = new ProductFournisseur($this-&amp;gt;db);&lt;br /&gt;
        if ($product-&amp;gt;fetch_product_fournisseur_price($line-&amp;gt;fk_fournprice))&lt;br /&gt;
            $line-&amp;gt;pa_ht = $product-&amp;gt;fourn_unitprice * (1 - $product-&amp;gt;fourn_remise_percent / 100);&lt;br /&gt;
        if (isset($conf-&amp;gt;global-&amp;gt;MARGIN_TYPE) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;MARGIN_TYPE == &amp;quot;2&amp;quot; &amp;amp;&amp;amp; $product-&amp;gt;fourn_unitcharges &amp;gt; 0)&lt;br /&gt;
            $line-&amp;gt;pa_ht += $product-&amp;gt;fourn_unitcharges;&lt;br /&gt;
    }&lt;br /&gt;
    // si prix d&#039;achat non renseigné et devrait l&#039;être, alors prix achat = prix vente&lt;br /&gt;
    if ((!isset($line-&amp;gt;pa_ht) || $line-&amp;gt;pa_ht == 0) &amp;amp;&amp;amp; $line-&amp;gt;subprice &amp;gt; 0 &amp;amp;&amp;amp; (isset($conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull == 1)) {&lt;br /&gt;
        $line-&amp;gt;pa_ht = $line-&amp;gt;subprice * (1 - ($line-&amp;gt;remise_percent / 100));&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the lines are filled with margin infos. The price has just been fetched here, and we will now modify it with the coefficient before the price gets used for margin computation.&lt;br /&gt;
&lt;br /&gt;
To optimize things up, we will load the custom fields before the loop (because they don&#039;t change between product/service lines, they are the custom fields of the parent object), and then apply the global_coefficient inside the loop.&lt;br /&gt;
&lt;br /&gt;
Just above the code we found (just before the for loop), paste the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
// Get object&#039;s id (either stored in property &amp;quot;id&amp;quot; or &amp;quot;rowid&amp;quot;)&lt;br /&gt;
$this_id = (!empty($this-&amp;gt;id)) ? $this-&amp;gt;id : $this-&amp;gt;rowid;&lt;br /&gt;
// Load custom fields records&lt;br /&gt;
$customfields = customfields_fill_object($this);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And now, just below the code we found, inside the loop (and just above the comment: &amp;quot;// calcul des marges&amp;quot;), paste the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
// Apply the coefficient&lt;br /&gt;
if (!empty($this-&amp;gt;customfields-&amp;gt;cf_global_coefficient)) {&lt;br /&gt;
    $line-&amp;gt;pa_ht = $line-&amp;gt;pa_ht * $this-&amp;gt;customfields-&amp;gt;cf_global_coefficient;&lt;br /&gt;
    $line-&amp;gt;subprice = $line-&amp;gt;subprice * $this-&amp;gt;customfields-&amp;gt;cf_global_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that here we cheated a bit: we do not compute the coefficient on the global total price, but rather per product/service line price. This is done on purpose, because mathematically this should produce no difference, and it also allows Dolibarr to continue peacefully with its margin calculations (else we would have to adapt all those computations, which would imply a lot more of code changes!).&lt;br /&gt;
&lt;br /&gt;
That&#039;s all, the price should be reflected in the margin. Just refresh the page to reflect the change.&lt;br /&gt;
&lt;br /&gt;
Note that this modification &#039;&#039;&#039;needs only to be done once for all modules&#039;&#039;&#039;, you then just have to create a custom field named &amp;quot;global_coefficient&amp;quot; in every module you want to apply this coefficient.&lt;br /&gt;
&lt;br /&gt;
== Refresh the price instantly ==&lt;br /&gt;
&lt;br /&gt;
Maybe you noticed one glitch: when you modify the global_coefficient, the global total price isn&#039;t updated until you add/edit a product/service line. This is because the update_price() function is only called when we add/edit a product/service line.&lt;br /&gt;
&lt;br /&gt;
To fix that, we need now to force the refreshing of price. To do that, we will use an &amp;quot;aftersave&amp;quot; overloading function, which will execute commands after our custom field is saved.&lt;br /&gt;
&lt;br /&gt;
Go to the folder /htdocs/customfields/fields and rename the file customfields_fields_extend.lib.php.example into customfields_fields_extend.lib.php .&lt;br /&gt;
&lt;br /&gt;
Now, open it in your favourite text editor, and add the following function :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_aftersave_facture_global_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value, $fields) {&lt;br /&gt;
    // Force refreshing total price&lt;br /&gt;
    $object-&amp;gt;update_price(0, &#039;auto&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s all. Now, when you edit your global_coefficient, this will force refresh the total price.&lt;br /&gt;
&lt;br /&gt;
However, you will still notice one last glitch: when you edit the global_coefficient, the total price isn&#039;t reflected instantly, you must refresh the webpage once to see the new price. This is because of how the datasheet and the CustomFields hook are designed: CustomFields is called only exactly where the custom fields are place, thus it is called after the total price, the margin and the remaining debt are printed. Thus, CustomFields updates the value of your custom field only after all those informations get printed, and thus it cannot modify them at this stage. That&#039;s why those information are out-of-date, and you must refresh the webpage once to get to see the latest values.&lt;br /&gt;
&lt;br /&gt;
There is a workaround for this: refresh automatically the webpage (but delete the POST data to avoid an infinite loop). You just need to use this overloading code instead of the one above:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_aftersave_facture_global_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value, $fields) {&lt;br /&gt;
    // Force refreshing total price&lt;br /&gt;
    $object-&amp;gt;update_price(0, &#039;auto&#039;);&lt;br /&gt;
    // Force refresh the page to update prices that were printed before the custom field aftersave was triggered (and only if not creating the object because then there&#039;s no need to refresh)&lt;br /&gt;
    if ($action !== &#039;add&#039;) print(&#039;&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;window.location.href = window.location.pathname + window.location.search;&amp;lt;/script&amp;gt;&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Another way to fix this issue would be to modify how CustomFields save the custom fields, by moving this stage at the very top. However, this would need another hook, which should be called at the very beginning of the page loading, which should be made only for this purpose (PageLoading), and should be available in all modules supported by CustomFields in order to not break anything.&lt;br /&gt;
&lt;br /&gt;
Note that you will need to create one overloading function for each module you want to support with the global_coefficient custom field. For example, if you want to support both invoices and commercial proposals, you will need two overloading functions:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient for invoices (note the &amp;quot;facture&amp;quot; in the name of the function)&lt;br /&gt;
function customfields_field_aftersave_facture_global_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value, $fields) {&lt;br /&gt;
    // Force refreshing total price&lt;br /&gt;
    $object-&amp;gt;update_price(0, &#039;auto&#039;);&lt;br /&gt;
    // Force refresh the page to update prices that were printed before the custom field aftersave was triggered (and only if not creating the object because then there&#039;s no need to refresh)&lt;br /&gt;
    if ($action !== &#039;add&#039;) print(&#039;&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;window.location.href = window.location.pathname + window.location.search;&amp;lt;/script&amp;gt;&#039;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Global coefficient for commercial proposals (note the &amp;quot;propal&amp;quot; in the name of the function)&lt;br /&gt;
function customfields_field_aftersave_propal_global_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value, $fields) {&lt;br /&gt;
    // Force refreshing total price&lt;br /&gt;
    $object-&amp;gt;update_price(0, &#039;auto&#039;);&lt;br /&gt;
    // Force refresh the page to update prices that were printed before the custom field aftersave was triggered (and only if not creating the object because then there&#039;s no need to refresh)&lt;br /&gt;
    if ($action !== &#039;add&#039;) print(&#039;&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;window.location.href = window.location.pathname + window.location.search;&amp;lt;/script&amp;gt;&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Modify the total price PER product/service line with a coefficient (without tampering the discount) =&lt;br /&gt;
&lt;br /&gt;
Here we will go through the creation and management of a custom field called &#039;&#039;&#039;coefficient&#039;&#039;&#039; that will change the total price of each product by modifying it by the coefficient.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will not create a coefficient per product but rather per product&#039;s line. Thus if you want to create a coefficient that never change between invoices but only depending on the product and stays static, please refer to the next chapter below about creating a custom tax per product.&lt;br /&gt;
&lt;br /&gt;
The preliminary and necessary step is to first create a custom field that will be used to modify the total price. We will create one for Client Invoice Lines.&lt;br /&gt;
&lt;br /&gt;
Go to the CustomFields administration panel, then into the Client Invoice Lines tab, then click on the button New Field, name the field &#039;&#039;&#039;coefficient&#039;&#039;&#039;, and set the type to &#039;&#039;&#039;Double&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
When you go to client invoices in Dolibarr, you should then see something similar to this:&lt;br /&gt;
[[File:Cfe1.jpg]]&lt;br /&gt;
You can see that our coefficient field was successfully created.&lt;br /&gt;
&lt;br /&gt;
Then here are two alternative ways of implementing the change of the total price of each product relativery to this coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change total price in database ==&lt;br /&gt;
&lt;br /&gt;
Here we will use a &amp;quot;save&amp;quot; overloading function to change the total price directly in the database, thus we won&#039;t have to modify anything else (eg: nor PDF templating nor ODT) since the total price will be already updated in the database.&lt;br /&gt;
&lt;br /&gt;
Warning: this method will work only with CustomFields =&amp;gt; 3.2.23 since the &amp;quot;save&amp;quot; overloading function did not work for products lines before.&lt;br /&gt;
&lt;br /&gt;
Just add the following overloading function in your customfields_fields_extend.lib.php file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_facturedet_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
 &lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $coefficient = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;coefficient&#039;}; // you could also use $coefficient = $value;&lt;br /&gt;
 &lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $coefficient = price2num($coefficient);&lt;br /&gt;
 &lt;br /&gt;
    // Get the original subprices (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
 &lt;br /&gt;
    // Update the subprices with the coefficient&lt;br /&gt;
    $object-&amp;gt;total_ht = $object-&amp;gt;total_ht * $coefficient; // compute no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva = $object-&amp;gt;total_tva * $coefficient; // compute tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc * $coefficient; // compute tax included price&lt;br /&gt;
 &lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(&#039;&#039;,true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
 &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;coefficient&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should show the correct total price on the Dolibarr&#039;s interface without modifying the unit subprice (P.U. Unit):&lt;br /&gt;
&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
You have nothing to edit in your PDF or ODT template, since the new subprice with applied coefficient will be automatically printed instead of the old one without coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change printed total price per product without tampering the subprice in the database ==&lt;br /&gt;
&lt;br /&gt;
Here is a way to change the printed total price (the one shown on screen and in PDF/ODT) but without changing the total price in the database.&lt;br /&gt;
&lt;br /&gt;
The following sub-chapters are all independent, thus you can only follow the ones that you require, eg: if you only need to change the price in your PDF documents, just create a custom field then jump to the sub-chapter to &#039;&#039;&#039;Implement the change of price in PDF&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in ODT templates ===&lt;br /&gt;
&lt;br /&gt;
There&#039;s no eval implementation currently inside ODT templates, but there is a kind of hook implementation, called [[Create_an_ODT_document_template#Other_personalized_tags|substitution functions]].&lt;br /&gt;
&lt;br /&gt;
You can either create your own module, then create your own substitution function, but for the sake of simplicity in this tutorial, we will directly use the CustomFields substitution function. However, please remember that you will have to redo your changes everytime you will update the CustomFields module, so if you don&#039;t want to do that it would be better if you create your own module and substitution function.&lt;br /&gt;
&lt;br /&gt;
Open the file &#039;&#039;&#039;htdocs/customfields/core/substitutions/functions_customfields.lib.php&#039;&#039;&#039; and go to the function &#039;&#039;&#039;customfields_completesubstitutionarray_lines&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
At the very end of this function, but inside the customfields check:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) {&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
But just before the ending bracket, you should add the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($object-&amp;gt;table_element_line === &#039;facturedet&#039;) { // First check that we are in the right module, we don&#039;t want to use coefficient for other modules if we didn&#039;t define the custom field there&lt;br /&gt;
    $substitutionarray[&#039;line_price_full&#039;] = $substitutionarray[&#039;line_price_ttc&#039;] * $substitutionarray[&#039;cf_coefficient&#039;]; // multiply the total price with our coefficient&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code will add a new tag &#039;&#039;&#039;{line_price_full}&#039;&#039;&#039; that you can then use inside your ODT templates.&lt;br /&gt;
&lt;br /&gt;
Just place this tag in a table in your ODT document, between the table tags ( [!-- BEGIN row.lines --] ... [!-- END row.lines --] ), and this will print your full price.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in PDF templates ===&lt;br /&gt;
&lt;br /&gt;
First, we will create our own PDF template by copying an existing template, and then we will edit it to change the price of items.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Creating your own PDF template by copying an existing template&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Go into the following folder: &#039;&#039;&#039;htdocs/core/modules/facture/doc/&#039;&#039;&#039;, then copy the file &#039;&#039;&#039;pdf_crabe.modules.php&#039;&#039;&#039; and name the new one &#039;&#039;&#039;pdf_crabecoeff.modules.php&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Then open it in your favourite editor, and change the following lines:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures // change pdf_crabe into pdf_crabecoeff&lt;br /&gt;
{&lt;br /&gt;
...&lt;br /&gt;
    function __construct($db)&lt;br /&gt;
    {&lt;br /&gt;
        ...&lt;br /&gt;
	$this-&amp;gt;name = &amp;quot;crabe&amp;quot;; // change &amp;quot;crabe&amp;quot; into &amp;quot;crabecoeff&amp;quot;&lt;br /&gt;
        ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you have your own PDF template!&lt;br /&gt;
&lt;br /&gt;
Now you should &#039;&#039;&#039;enable your PDF template&#039;&#039;&#039; before doing anything else, by going into the Modules Administration panel, and into the Invoice admin page, then click on the ON button at the right of the &#039;&#039;&#039;crabecoeff&#039;&#039;&#039; entry to enable the template.&lt;br /&gt;
&lt;br /&gt;
Now, let&#039;s proceed onto modifying the prices.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Changing the prices automatically in your PDF&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
You can now do pretty much anything you want in your PDF template, but I will give you a mean to automatically change the prices of every products with our coefficient, as well as the total price, without editing too much the template.&lt;br /&gt;
&lt;br /&gt;
First, try to find the following code of line (should be inside the write_file() method):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf=pdf_getInstance($this-&amp;gt;format);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, just copy/paste the following code &#039;&#039;&#039;below&#039;&#039;&#039; pdf_getInstance():&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$this-&amp;gt;customfields = customfields_fill_object($object, null, $outputlangs, null, true); // beautified values&lt;br /&gt;
$this-&amp;gt;customfields_raw = customfields_fill_object($object, null, $outputlangs, &#039;raw&#039;, null); // raw values&lt;br /&gt;
$this-&amp;gt;customfields_lines = customfields_fill_object_lines($object, null, $outputlangs, null, true); // product lines&#039; values&lt;br /&gt;
&lt;br /&gt;
// Resetting every $object price because we will compute them anew&lt;br /&gt;
$object-&amp;gt;total_ht = 0;&lt;br /&gt;
$object-&amp;gt;total_tva = 0;&lt;br /&gt;
$object-&amp;gt;total_ttc = 0;&lt;br /&gt;
// For each line of product/service, we will recompute all the prices&lt;br /&gt;
foreach($object-&amp;gt;lines as $line) {&lt;br /&gt;
    // Get the coefficient custom field&#039;s value for the current line&lt;br /&gt;
    $coeff = $object-&amp;gt;customfields-&amp;gt;lines-&amp;gt;{$line-&amp;gt;rowid}-&amp;gt;cf_coefficient;&lt;br /&gt;
    // Recompute the price of this product/service line&lt;br /&gt;
    $line-&amp;gt;total_ht = $line-&amp;gt;total_ht*$coeff; // compute no tax price&lt;br /&gt;
    $line-&amp;gt;total_tva = $line-&amp;gt;total_tva*$coeff; // compute tax amount&lt;br /&gt;
    $line-&amp;gt;total_ttc = $line-&amp;gt;total_ttc*$coeff; // compute tax included price&lt;br /&gt;
    // Do the sum of all new products/services prices and tva (will be incremented with every product/service line)&lt;br /&gt;
    $object-&amp;gt;total_ht += $line-&amp;gt;total_ht; // global no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva += $line-&amp;gt;total_tva; // global vat/tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc += $line-&amp;gt;total_ttc; // global  tax included price&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you should try to generate your PDF document, and you should get something like this:&lt;br /&gt;
[[File:Cfe3.jpg]]&lt;br /&gt;
&lt;br /&gt;
Note: printing the coefficient as a column in your PDF document is also possible but will not be covered here because PDF templates will vary for every persons depending on your needs.&lt;br /&gt;
For guidelines about how to do this, you can read the documentation on [[Create_a_PDF_document_template|how to create a PDF template]] and [[Module_CustomFields#Implementing_in_PDF_templates|how to use custom fields in PDF templates]].&lt;br /&gt;
&lt;br /&gt;
Hint: you should be looking inside the following conditional block, inside the write_file() method:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
{&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of total price in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
Unluckily there is not (yet) any hooking context to modify how lines of products/services are printed, thus the only thing you can do is directly edit the &#039;&#039;&#039;template file&#039;&#039;&#039; and manually put inside your code to load your custom field and print it the way you want using HTML formatting, for our example:&lt;br /&gt;
&lt;br /&gt;
* For Dolibarr 3.3 and above, edit the file &#039;&#039;&#039;htdocs/core/tpl/objectline_view.tpl.php&#039;&#039;&#039;&lt;br /&gt;
* For Dolibarr 3.1 and 3.2, edit the file &#039;&#039;&#039;htdocs/core/tpl/freeproductline_view.tpl.php&#039;&#039;&#039; AND &#039;&#039;&#039;predefinedproductline_view.tpl.php&#039;&#039;&#039; (apply the same changes to both files)&lt;br /&gt;
&lt;br /&gt;
In the template file you opened, find the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And replace it by:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $object-&amp;gt;table_element_line; // you can use $object-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
&lt;br /&gt;
// Modifying the HT price and printing it on the Dolibarr&#039;s datasheet interface&lt;br /&gt;
// Applying a multiplying/dividing coefficient to an HT price will give a mathematically correct TTC price since the taxes are also a multiplier&lt;br /&gt;
// However, if you want to apply other mathematical operations (like addition or substaction), you should use the total price with $line-&amp;gt;total_ttc&lt;br /&gt;
if (!empty($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
    echo price($line-&amp;gt;total_ht * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient);&lt;br /&gt;
} else {&lt;br /&gt;
    echo price($line-&amp;gt;total_ht);&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should produce something like this:&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Technical note: here we are using customfields_fill_object() instead of customfields_fill_object_lines() (the latter being specifically adapted to lines and thus should theoretically be used here) for performance reasons because the template file we are editing is called in a loop, and customfields_fill_object_lines() should be called outside a loop (because it already does the job that is done by the loop), so here we &#039;tricks&#039; the customfields_fill_object() function to avoid this loop by submitting &#039;facturedet&#039; as a module by itself (when in fact it&#039;s a product lines table, but it works the same way nevertheless). This is a good demonstration of the flexibility of the CustomFields functions and methods.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the coefficient column in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
It&#039;s also possible to show the coefficient in a new column on the Dolibarr interface.&lt;br /&gt;
&lt;br /&gt;
To do that, you need to do two things:&lt;br /&gt;
&lt;br /&gt;
1- Add the column title &amp;quot;Coeff.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
2- Add the generator that will input the coefficient value for each line.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Add the column title &amp;quot;Coeff.&amp;quot;&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
Open in your favourite editor the file /htdocs/core/class/commonobject.class.php and inside the function printObjectLines (note the plural s), find the following lines of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Total HT&lt;br /&gt;
print &#039;&amp;lt;td align=&amp;quot;right&amp;quot; width=&amp;quot;50&amp;quot;&amp;gt;&#039;.$langs-&amp;gt;trans(&#039;TotalHTShort&#039;).&#039;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is what prints the Total HT price column. We will place the Coeff column just before.&lt;br /&gt;
&lt;br /&gt;
So, just above this code, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
dol_include_once(&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element_line);&lt;br /&gt;
$coeff_field = $customfields-&amp;gt;fetchFieldStruct(&#039;coefficient&#039;);&lt;br /&gt;
if (!empty($coeff_field)) {&lt;br /&gt;
    print &#039;&amp;lt;td align=&amp;quot;right&amp;quot; width=&amp;quot;50&amp;quot;&amp;gt;&#039;.$langs-&amp;gt;trans(&#039;Coeff.&#039;).&#039;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will take care of showing the Coeff. column, but only for modules where you created such a custom field (because the file we modified is a common object for all modules in Dolibarr, not just invoices, thus this modification will gracefully skip this custom field if it does not exists for other modules).&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Add the generator&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/tpl/objectline_view.tpl.php and try to find the following block of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php if ($line-&amp;gt;special_code == 3)	{ ?&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo $langs-&amp;gt;trans(&#039;Option&#039;); ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } else { ?&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is what prints the total HT price value for each line.&lt;br /&gt;
&lt;br /&gt;
Just above of this block of code, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $object-&amp;gt;table_element_line; // you can use $this-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
if (isset($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
    &amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient; ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just like for the column title, this code will take care of generating the coefficient value only if the custom field is defined, because this file is also a generic template for all modules of Dolibarr.&lt;br /&gt;
&lt;br /&gt;
At this point, everything is done, you should see the following:&lt;br /&gt;
[[File:Cf_wiki_coeff_column.png]]&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in margin module ===&lt;br /&gt;
&lt;br /&gt;
If you enabled the margin module, you should see that the coefficient is not applied there, but it&#039;s easy to fix that.&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/class/commonobject.class.php and find the function getMarginInfos(), then inside find the following lines:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach($this-&amp;gt;lines as $line) {&lt;br /&gt;
    if (empty($line-&amp;gt;pa_ht) &amp;amp;&amp;amp; isset($line-&amp;gt;fk_fournprice) &amp;amp;&amp;amp; !$force_price) {&lt;br /&gt;
        $product = new ProductFournisseur($this-&amp;gt;db);&lt;br /&gt;
        if ($product-&amp;gt;fetch_product_fournisseur_price($line-&amp;gt;fk_fournprice))&lt;br /&gt;
            $line-&amp;gt;pa_ht = $product-&amp;gt;fourn_unitprice * (1 - $product-&amp;gt;fourn_remise_percent / 100);&lt;br /&gt;
        if (isset($conf-&amp;gt;global-&amp;gt;MARGIN_TYPE) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;MARGIN_TYPE == &amp;quot;2&amp;quot; &amp;amp;&amp;amp; $product-&amp;gt;fourn_unitcharges &amp;gt; 0)&lt;br /&gt;
            $line-&amp;gt;pa_ht += $product-&amp;gt;fourn_unitcharges;&lt;br /&gt;
    }&lt;br /&gt;
    // si prix d&#039;achat non renseigné et devrait l&#039;être, alors prix achat = prix vente&lt;br /&gt;
    if ((!isset($line-&amp;gt;pa_ht) || $line-&amp;gt;pa_ht == 0) &amp;amp;&amp;amp; $line-&amp;gt;subprice &amp;gt; 0 &amp;amp;&amp;amp; (isset($conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull == 1)) {&lt;br /&gt;
        $line-&amp;gt;pa_ht = $line-&amp;gt;subprice * (1 - ($line-&amp;gt;remise_percent / 100));&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the lines are filled with margin infos. The price has just been fetched here, and we will now modify it with the coefficient before the price gets used for margin computation.&lt;br /&gt;
&lt;br /&gt;
Just below the code we found, paste the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $line-&amp;gt;table_element;&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
if (!empty($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
    $line-&amp;gt;pa_ht = $line-&amp;gt;pa_ht * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient;&lt;br /&gt;
    $line-&amp;gt;subprice = $line-&amp;gt;subprice * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s all, the price should be reflected in the margin.&lt;br /&gt;
[[File:Cf_wiki_coeff_margin.png]]&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in remaining debt to pay ===&lt;br /&gt;
&lt;br /&gt;
Open /htdocs/core/class/commonobject.class.php and look for the function update_price().&lt;br /&gt;
&lt;br /&gt;
You should see the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function update_price($exclspec=0,$roundingadjust=&#039;none&#039;,$nodatabaseupdate=0,$seller=&#039;&#039;)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just below the include_once line, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;;&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/conf/conf_customfields_func.lib.php&#039;;&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $this-&amp;gt;table_element_line);&lt;br /&gt;
// Fetching line ids from parent object id&lt;br /&gt;
$line_ids = $customfields-&amp;gt;fetchAny(&#039;rowid&#039;, MAIN_DB_PREFIX.$this-&amp;gt;table_element_line, $this-&amp;gt;fk_element.&#039; = &#039;.$this-&amp;gt;id);&lt;br /&gt;
$line_ids = array_values_recursive(&#039;rowid&#039;, $line_ids);&lt;br /&gt;
// Load custom fields&lt;br /&gt;
$cf_records = $customfields-&amp;gt;fetch($line_ids);&lt;br /&gt;
// Reassociate the records with the line id (instead of the customfields record id)&lt;br /&gt;
$cf_line_cfrowid_to_rowid = array_reassociate(&#039;rowid&#039;, &#039;fk_&#039;.$this-&amp;gt;table_element_line, $cf_records);&lt;br /&gt;
$tmp = $cf_records;&lt;br /&gt;
$cf_records = array();&lt;br /&gt;
foreach ($tmp as $k=&amp;gt;$v) { $cf_records[$cf_line_cfrowid_to_rowid[$k]] = $tmp[$k]; }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will load the coefficient custom field (and also any other custom field you have defined for lines).&lt;br /&gt;
&lt;br /&gt;
Now scroll below, and you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
while ($i &amp;lt; $num)&lt;br /&gt;
{&lt;br /&gt;
    $obj = $this-&amp;gt;db-&amp;gt;fetch_object($resql);&lt;br /&gt;
&lt;br /&gt;
    // Note: There is no check on detail line and no check on total, if $forcedroundingmode = &#039;none&#039;&lt;br /&gt;
&lt;br /&gt;
    if ($forcedroundingmode == &#039;0&#039;)	// Check if data on line are consistent. This may solve lines that were not consistent because set with $forcedroundingmode=&#039;auto&#039;&lt;br /&gt;
    {&lt;br /&gt;
        $localtax_array=array($obj-&amp;gt;localtax1_type,$obj-&amp;gt;localtax1_tx,$obj-&amp;gt;localtax2_type,$obj-&amp;gt;localtax2_tx);&lt;br /&gt;
        $tmpcal=calcul_price_total($obj-&amp;gt;qty, $obj-&amp;gt;up, $obj-&amp;gt;remise_percent, $obj-&amp;gt;vatrate, $obj-&amp;gt;localtax1_tx, $obj-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $obj-&amp;gt;info_bits, $obj-&amp;gt;product_type, $seller, $localtax_array);&lt;br /&gt;
        $diff=price2num($tmpcal[1] - $obj-&amp;gt;total_tva, &#039;MT&#039;, 1);&lt;br /&gt;
        if ($diff)&lt;br /&gt;
        {&lt;br /&gt;
            $sqlfix=&amp;quot;UPDATE &amp;quot;.MAIN_DB_PREFIX.$this-&amp;gt;table_element_line.&amp;quot; SET &amp;quot;.$fieldtva.&amp;quot; = &amp;quot;.$tmpcal[1].&amp;quot;, total_ttc = &amp;quot;.$tmpcal[2].&amp;quot; WHERE rowid = &amp;quot;.$obj-&amp;gt;rowid;&lt;br /&gt;
            dol_syslog(&#039;We found unconsistent data into detailed line (difference of &#039;.$diff.&#039;) for line rowid = &#039;.$obj-&amp;gt;rowid.&amp;quot; (total vat of line calculated=&amp;quot;.$tmpcal[1].&amp;quot;, database=&amp;quot;.$obj-&amp;gt;total_tva.&amp;quot;). We fix the total_vat and total_ttc of line by running sqlfix = &amp;quot;.$sqlfix);&lt;br /&gt;
            $resqlfix=$this-&amp;gt;db-&amp;gt;query($sqlfix);&lt;br /&gt;
            if (! $resqlfix) dol_print_error($this-&amp;gt;db,&#039;Failed to update line&#039;);&lt;br /&gt;
            $obj-&amp;gt;total_tva = $tmpcal[1];&lt;br /&gt;
            $obj-&amp;gt;total_ttc = $tmpcal[2];&lt;br /&gt;
            //&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just after, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
$cf_coeff = $cf_records[$obj-&amp;gt;rowid]-&amp;gt;coefficient;&lt;br /&gt;
if (empty($cf_coeff) and $cf_coeff !== 0) $cf_coeff = 1; // by default, an empty coefficient = 1&lt;br /&gt;
$obj-&amp;gt;total_ht = $obj-&amp;gt;total_ht * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_tva = $obj-&amp;gt;total_tva * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_localtax1 = $obj-&amp;gt;total_localtax1 * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_localtax2 = $obj-&amp;gt;total_localtax2 * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_ttc = $obj-&amp;gt;total_ttc * $cf_coeff;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should do the trick. Now go to one of your invoices (or whatever module you are targetting), and try to add/edit a product/service line to force refresh the total price. You should then see this:&lt;br /&gt;
[[File:Cf_wiki_coeff_remaining_debt.png]]&lt;br /&gt;
&lt;br /&gt;
= Custom tax per product =&lt;br /&gt;
&lt;br /&gt;
This part will show you how to create a static custom tax per product. Static means that the tax will be stored per product, and not per product&#039;s line, thus you just have to setup the tax once per product and it will always be applied in every invoice you add this product.&lt;br /&gt;
&lt;br /&gt;
To do that, you will need the CustomFields Pro module + ProductsEasyAccess.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
1- Create a product&#039;s custom fields called &amp;quot;custom_tax&amp;quot;. Now, using ProductsEasyAccess, you can access this field wherever you wants, either to show it on the Dolibarr&#039;s interface (see previous chapter), or in a PDF template, which we will describe below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2- Modify your invoice&#039;s PDF template. You first need to load all the products&#039; fields &#039;&#039;before&#039;&#039; the loop over the products&#039; lines. To do this, search for a line like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// This should be placed above the &amp;quot;loop lines&amp;quot; below, add this:&lt;br /&gt;
&lt;br /&gt;
dol_include_once(&#039;/productseasyaccess/lib/productseasyaccess.lib.php&#039;);&lt;br /&gt;
fill_object_with_products_fields($object);&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines, search for this command&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  // Add this just below the for loop, this will ease your php manipulation after.&lt;br /&gt;
  // This will allow you to more easily access the products&#039; fields (this will store the id of each product inside $lineproductid).&lt;br /&gt;
  $line_product_id = $object-&amp;gt;lines[$i]-&amp;gt;fk_product;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4- You can now use your products&#039; fields in your PDF. To access the custom_tax field, just use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And to print it in your PDF:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell(0,3, $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax, 0, &#039;L&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5- Then, if you want to count the custom tax total and add it to your vat total, you can create a counter inside the for loop we cited in the first step:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_custom_tax = 0; // Initialize the custom tax total counter&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  $object-&amp;gt;total_custom_tax += $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then find inside the function **_tableau_tot**, find the following line:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And prepend the total modification like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc + $object-&amp;gt;total_custom_tax; // prepend this to add the custom tax to the total&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Done. This should show your custom tax per product and your total including all custom taxes.&lt;/div&gt;</summary>
		<author><name>Lrq3000</name></author>
	</entry>
	<entry>
		<id>https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Dev&amp;diff=33180</id>
		<title>Module CustomFields Dev</title>
		<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Dev&amp;diff=33180"/>
		<updated>2015-02-24T14:15:49Z</updated>

		<summary type="html">&lt;p&gt;Lrq3000: /* Never/Maybe one day  */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:CustomFields]]&lt;br /&gt;
{{TemplateDocDevEn}}&lt;br /&gt;
{{TemplateModEN}}&lt;br /&gt;
{{BasculeDevUserEn|&lt;br /&gt;
name=CustomFields|&lt;br /&gt;
num=8500|&lt;br /&gt;
devdoc=This page|&lt;br /&gt;
userdoc=[[Module_CustomFields]]|}}&lt;br /&gt;
[[Category:FAQ EN]]&lt;br /&gt;
&lt;br /&gt;
These notes are aimed to developpers who want to extend the functionnalities of the module or want to use it for extreme cases.&lt;br /&gt;
This is not meant to guide implementers on how to implement and configure the module (see the related chapter for that).&lt;br /&gt;
&lt;br /&gt;
If you are an implementer and just want to use CustomFields with its normal functions, you can read the [[Module_CustomFields|user documentation of CustomFields]].&lt;br /&gt;
&lt;br /&gt;
You can also jump directly to [[Module_CustomFields_Cases]] wiki page for a collection of practical examples.&lt;br /&gt;
&lt;br /&gt;
= Principles =&lt;br /&gt;
&lt;br /&gt;
CustomFields is simply a wrapper for SQL databases.&lt;br /&gt;
&lt;br /&gt;
The module is based on SQL to manage the fields and most of the stuff. It uses and understands standard SQL.&lt;br /&gt;
&lt;br /&gt;
The main principles of CustomFields are:&lt;br /&gt;
&lt;br /&gt;
* one SQL column = one Dolibarr field.&lt;br /&gt;
* one SQL type = one method to show and edit the field in Dolibarr.&lt;br /&gt;
* almost everything should be configurable from the configuration file, or admin interface.&lt;br /&gt;
* be independent from Dolibarr&#039;s data structures.&lt;br /&gt;
&lt;br /&gt;
CustomFields sits besides Dolibarr, and does not mix with it, even if in the Dolibarr&#039;s interface it looks like CustomFields is embedded inside Dolibarr, in reality, CustomFields is totally independent.&lt;br /&gt;
&lt;br /&gt;
= Goals =&lt;br /&gt;
&lt;br /&gt;
This module was made to provide an easy way to manage custom sql fields, not only for users and implementers, but also for developpers to use in their own module:&lt;br /&gt;
* by easily adding the support of custom fields for your own module by using this module;&lt;br /&gt;
* by reusing custom fields from another module in your own module;&lt;br /&gt;
* by managing sql fields in your own module by using the CustomFields class (which will take care of everything from printing, editing to PDF/ODT templating).&lt;br /&gt;
&lt;br /&gt;
So this module is not only an auxiliary of other modules, but it can also be the core part of your own module by using it as your SQL query class for fields.&lt;br /&gt;
&lt;br /&gt;
= SQL structure =&lt;br /&gt;
&lt;br /&gt;
A CustomFields table needs to respect three parameters that never change:&lt;br /&gt;
&lt;br /&gt;
* table&#039;s name (format: llx_*dolibarrmodule*_customfields where *dolibarrmodule* = table_element in conf_customfields.lib.php)&lt;br /&gt;
* first column: rowid, which is the primary key for each field&lt;br /&gt;
* second column: fk_*dolibarrmodule* which is the foreign key that links every customfields record to the Dolibarr&#039;s module that is associated (invoice, order, etc...).&lt;br /&gt;
&lt;br /&gt;
Apart from the table&#039;s name and the first two special columns, every other columns are just standard sql columns that you can freely create, edit or delete as you want, from the module&#039;s administration page or from a third-party SQL management tool (such as phpMyAdmin).&lt;br /&gt;
&lt;br /&gt;
You can also create more complex sql fields: constraints, foreign keys, transformations, other sql types, etc...).&lt;br /&gt;
&lt;br /&gt;
Then the module will try to manage the sql fields you manually created the best it can:&lt;br /&gt;
* by printing appropriate input fields in the Dolibarr gui interface.&lt;br /&gt;
* by providing the developper an easy access to these fields in php (eg: $customfields-&amp;gt;cf_myfield or $object-&amp;gt;customfields-&amp;gt;cf_myfield) for any use, including PDF templates and ODT templates, or in any php script (so that you can use these fields in your own module).&lt;br /&gt;
&lt;br /&gt;
= SQL Indexes =&lt;br /&gt;
&lt;br /&gt;
The module automatically adds an index on the primary key and foreign key (first and second columns).&lt;br /&gt;
&lt;br /&gt;
To further optimize the performances of your database, and particularly the customfields tables, you may want to manually add indexes based on usage.&lt;br /&gt;
&lt;br /&gt;
Also, you may add indexes on remote tables used by constrained fields, which could optimize even more the performances since constrained fields have the worst performance drawback for CustomFields.&lt;br /&gt;
&lt;br /&gt;
= SQL Compatibility mode =&lt;br /&gt;
&lt;br /&gt;
The module relies on referential integrity checks and foreign keys of your DBMS to keep the database clean and to provide the constrained fields feature. Referential integrity checks is part of the SQL standards and should be used whenever possible, which always was a main goal for CustomFields (to be respectful of SQL standards).&lt;br /&gt;
&lt;br /&gt;
Unluckily, a few DBMS systems still don&#039;t support this critical functionality of the SQL standard, for example MySQL enables MyISAM engine by default, which doesn&#039;t support this feature.&lt;br /&gt;
&lt;br /&gt;
In these cases, you can either do two things:&lt;br /&gt;
* Switch to an engine which support referential integrity. For example with MySQL, you can enable InnoDB, which support referential integrity checks and foreign keys.&lt;br /&gt;
* Use CustomFields compatibility mode (more infos below).&lt;br /&gt;
&lt;br /&gt;
In the case your database does not support referential integrity checks, CustomFields will automatically switch to the SQL compatibility mode, which will allow you to use CustomFields even on databases where foreign keys are not possible.&lt;br /&gt;
&lt;br /&gt;
In compatibility mode, you will get nearly all CustomFields advanced features such as Constrained fields, but the customfields&#039; tables in the database won&#039;t be cleaned automatically (no delete on cascade, meaning that when you delete an object, the linked custom fields will still be kept in your database), and constrained fields won&#039;t be updated automatically on update.&lt;br /&gt;
&lt;br /&gt;
Nevertheless, these missing cleanup functionalities won&#039;t be seen by your users because they will never be accessible: on deletion of an object, the custom fields won&#039;t ever be accessible again from Dolibarr&#039;s interface, and object&#039;s ids are never changed since Dolibarr also relies on this. Thus in compatibility mode, CustomFields will act just like normally, but your database will be a bit less clean.&lt;br /&gt;
&lt;br /&gt;
= JSON extension for extra options =&lt;br /&gt;
&lt;br /&gt;
== Introduction to JSON in CustomFields ==&lt;br /&gt;
The relational model is very great to store and fetch data with a declarative language, and this is very powerful, but this model also imposes a few limitations, for example the relations must be well formatted and predefined.&lt;br /&gt;
&lt;br /&gt;
That&#039;s why we finally implemented a &#039;&#039;&#039;JSON extension&#039;&#039;&#039; to manage &#039;&#039;&#039;extra options&#039;&#039;&#039; and associate them with custom fields. This allows to add many properties (or columns if you are more used to the relational model) on-the-fly without having to predefine them prior to storing and using them.&lt;br /&gt;
&lt;br /&gt;
This allows to add an undefined number of columns/properties/extra options that will help manage special behaviours for custom fields. For example, it is currently used to allow to change the order of appearance of custom fields, and to store and manage remote table and columns datas for constrained fields on DBMS that do not support Foreign Keys checks.&lt;br /&gt;
&lt;br /&gt;
We chose JSON over XML because the library are simpler and the data footprint smaller, and because CustomFields does not need advanced features of XML like XQuery, data typing and meta structures (at least not for now).&lt;br /&gt;
&lt;br /&gt;
The other advantage is that php objects can directly be encoded into JSON without any preprocessing, and can then as straightly be decoded from JSON back into a php object.&lt;br /&gt;
&lt;br /&gt;
A simple mean to encode and decode back an object into and from a storage place, as well as allow users to easily add/remove extra options were the main requirement, and JSON (+ JSON libraries for PHP) meet this criteria.&lt;br /&gt;
&lt;br /&gt;
However, note that the extra options are now encoded as an associative array, so as to avoid any ambiguities (there&#039;s no associative array in JSON, they are converted to objects).&lt;br /&gt;
&lt;br /&gt;
== Implementation of JSON ==&lt;br /&gt;
The implementation is very simple: a table called &#039;&#039;&#039;llx_customfields_extraoptions&#039;&#039;&#039; is created when you enable the CustomFields module, and this table only contains three columns:&lt;br /&gt;
* table_name (varchar)&lt;br /&gt;
* column_name (varchar)&lt;br /&gt;
* extraoptions (blob)&lt;br /&gt;
&lt;br /&gt;
(table_name, column_name) both forms the primary key of the table, and links to columns (custom fields) of other customfields tables.&lt;br /&gt;
extraoptions is a blob, which allows to store the data as binary (as-is) without encoding (which solves a big problem of JSON which is encoding special and foreign characters, here there is no problem at all).&lt;br /&gt;
&lt;br /&gt;
When fetching custom fields, the module simply JOIN the extraoptions table, which adds an &#039;&#039;&#039;extraoptions&#039;&#039;&#039; column for each custom field in the SQL result. Then it is quite easy to just json_decode() the data returned inside the extraoptions column.&lt;br /&gt;
&lt;br /&gt;
This methodology allows to have all the advantages of a relational database, with the flexibility of a free-schema model:&lt;br /&gt;
* everything is stored inside the database (only one storage place)&lt;br /&gt;
* fixed columns allows for relational joining and optimization&lt;br /&gt;
* thus queries (fetching) are optimised&lt;br /&gt;
* at the same time, the extraoptions are automatically retrieved and associated with every custom field, and whenever needed they can be used&lt;br /&gt;
* and thus we also get a flexible model to &#039;&#039;&#039;add extra (virtual) columns whenever needed&#039;&#039;&#039; without having to alter the relational database schema&lt;br /&gt;
&lt;br /&gt;
Thus no critical information is and should be stored inside the extraoptions column because they would not be query-able, only extra options that are just used to tweak the management of those fields inside the PHP application should be stored. But this is a wonderful way to add non critical informations that still allows to develop more complex features and management for special custom fields.&lt;br /&gt;
&lt;br /&gt;
== CustomFields JSON API ==&lt;br /&gt;
&lt;br /&gt;
=== Storing extra options ===&lt;br /&gt;
&lt;br /&gt;
You can use the &#039;&#039;&#039;setExtra()&#039;&#039;&#039; method to set extra options of a custom field.&lt;br /&gt;
&lt;br /&gt;
Eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$extra = array(); // $extra should always be an associative array, it&#039;s no longer an object&lt;br /&gt;
$extra[&#039;myproperty&#039;] = &#039;Anything I want here&#039;; // set a property/extra option like this&lt;br /&gt;
$extra[&#039;category&#039;][&#039;subcategory&#039;][&#039;prop&#039;] = true; // we can set recursive properties, there&#039;s not any limit to how deep the hierarchy can be&lt;br /&gt;
&lt;br /&gt;
require_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomField($db, &#039;currentmodule&#039;);&lt;br /&gt;
$fieldid = &#039;mycustomfields&#039;; // $fieldid can either be a column_name or an ordinal_position from information_schema.columns&lt;br /&gt;
$result = $customfields-&amp;gt;setExtra($fieldid, $extra); // store the extra options inside the database&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can also use addCustomField() or updateCustomField() with an $extra array, and these functions will also call the setExtra() method.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: the extra options you set are &#039;&#039;&#039;appended&#039;&#039;&#039; to the ones already stored in the database. However, you can also overwrite previous options, just use the same name of property but with a different value (the replacement is recursive, you can do it at any level in the hierarchy).&lt;br /&gt;
&lt;br /&gt;
=== Fetching extra options ===&lt;br /&gt;
&lt;br /&gt;
Extra options are automatically fetched along with a custom field structure, decoded, and stored inside the extra sub-property of any field object, or CustomFields object.&lt;br /&gt;
&lt;br /&gt;
When fetched, extra options are stored as a sub-object for the current field object (which itself is stored as a subobject of the CustomFields object if caching is enabled).&lt;br /&gt;
&lt;br /&gt;
Eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
require_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomField($db, &#039;currentmodule&#039;);&lt;br /&gt;
$fields = fetchAllFieldsStruct(); // fetching fields structure will automatically fetch extra options at the same time. This works with fetchAllFieldsStruct() and fetchFieldStruct()&lt;br /&gt;
&lt;br /&gt;
// Now that you fetched the fields structure, you can just access the extra options sub-properties:&lt;br /&gt;
print_r($fields-&amp;gt;mycustomfield-&amp;gt;extra); // will print all extra options for the field mycustomfield&lt;br /&gt;
print_r($customfields-&amp;gt;fields-&amp;gt;mycustomfield-&amp;gt;extra); // you can also access the same field(s) directly from the CustomFields object if caching is enabled (default behaviour)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Modifying extra options ===&lt;br /&gt;
&lt;br /&gt;
You can just combine the two previous points to edit extra options of a custom field: just load a custom field, edit on-the-fly the extra property with PHP code, and then store it back directly into the database:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
require_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomField($db, &#039;currentmodule&#039;);&lt;br /&gt;
$fields = fetchAllFieldsStruct();&lt;br /&gt;
&lt;br /&gt;
$fields-&amp;gt;mycustomfield-&amp;gt;extra[&#039;newproperty&#039;] = &#039;This is a new property&#039;; // add a new property over the old extra options sub-object&lt;br /&gt;
$fields-&amp;gt;mycustomfield-&amp;gt;extra[&#039;mycategory&#039;] = &#039;Overwrite the old category&#039;; // this will replace the previous value of &#039;mycategory&#039; extra option&lt;br /&gt;
&lt;br /&gt;
$customfields-&amp;gt;setExtra(&#039;mycustomfield&#039;, $fields-&amp;gt;mycustomfield-&amp;gt;extra); // store the modified extra options back into the database for future usage&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= CustomFields API =&lt;br /&gt;
&lt;br /&gt;
== Introduction to CustomFields API ==&lt;br /&gt;
&lt;br /&gt;
This chapter will cover the basics on how to manually and concretely use the CustomFields class to create/update/fetch/manage custom fields.&lt;br /&gt;
&lt;br /&gt;
This will give you a better understanding of how CustomFields works, as well as giving you insights on how to use it for your own applications.&lt;br /&gt;
&lt;br /&gt;
Important note: contrary to the Facade API, the core CustomFields class use columns names without any prefix, so don&#039;t try to use the prefix, it won&#039;t work! For example: if your field is named &#039;mycustomfield&#039; and prefix is &#039;cf_&#039;, when you use the Facade API you should use &#039;$object-&amp;gt;$path-&amp;gt;cf_mycustomfield&#039; but when you use the core CustomFields class directly, $fieldname = &#039;mycustomfield&#039;.&lt;br /&gt;
&lt;br /&gt;
== Prerequisite to use the CustomFields class ==&lt;br /&gt;
&lt;br /&gt;
Before using the CustomFields class, you first must instanciate it. This is a necessary preliminary step before using any of the methods that will be covered inside this chapter, and thus will not be repeated, only shown once here.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Include the CustomFields class (which contains the main API)&lt;br /&gt;
require_once(dirname(__FILE__).&#039;/../class/customfields.class.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Instanciate a CustomFields object&lt;br /&gt;
global $db; // the database handler created by Dolibarr&lt;br /&gt;
$table_element = &#039;currentmodule&#039;; // where $table_element is the database&#039;s table name for the module, without the Dolibarr prefix (eg: for &#039;llx_facture&#039;, table_element is &#039;facture&#039;). You can also find it inside the $object properties (just print_r() it).&lt;br /&gt;
$customfields = new CustomFields($db, $table_element); // instanciate the CustomFields class inside $customfields object&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
CustomFields keeps track of errors that can happen when executing methods and SQL queries inside the &#039;&#039;&#039;error&#039;&#039;&#039; and &#039;&#039;&#039;errors&#039;&#039;&#039; properties, which you can print with the printErrors() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Do something with CustomFields before&lt;br /&gt;
&lt;br /&gt;
if (count($customfields-&amp;gt;errors) &amp;gt; 0) { // If there is at least one error&lt;br /&gt;
    $customfields-&amp;gt;printErrors(); // print errors&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// You can also print your own error message, in two manners:&lt;br /&gt;
&lt;br /&gt;
// 1- submit your own message&lt;br /&gt;
$customfields-&amp;gt;printErrors(&#039;Your own error message here&#039;); // this will print ONLY your error message, using the Dolibarr&#039;s AJAX error dialog (standard way of printing errors)&lt;br /&gt;
&lt;br /&gt;
// 2- append your message in the customfields errors&lt;br /&gt;
$customfields-&amp;gt;errors[] = &#039;Your own error message to be appended with other CustomFields messages&#039;;&lt;br /&gt;
$customfields-&amp;gt;printErrors(); // Here your error message will be appended, thus all error messages will be printed. This allows you for example to add more intelligible informations on a specific error.&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== General purpose SQL methods ==&lt;br /&gt;
&lt;br /&gt;
CustomFields provide an interface to easily execute various common SQL requests, as well as your own custom SQL requests.&lt;br /&gt;
&lt;br /&gt;
* fetchAllTables() fetch a list of all the tables in the database&lt;br /&gt;
* fetchPrimaryField($table) fetch the column_name (and only the column_name!) of the primary field of a table. You can then use fetchFieldStruct()  with the column_name to get any information you want.&lt;br /&gt;
* fetchAny($columns, $table, $where=&#039;&#039;, $orderby=&#039;&#039;, $limitby=&#039;&#039;) allows you to issue any simple SQL query command, for example fetchAny(&#039;*&#039;, &#039;sometable&#039;) is equivalent to SELECT * FROM sometable; (but then everything is managed automatically and result is returned to you)&lt;br /&gt;
* executeSQL($sql, $eventname) to execute any SQL query you want, this will return you a resource (and NOT a processed nuplet like fetchAny!). $eventname can be anything, it&#039;s just for Dolibarr logging facility.&lt;br /&gt;
* executeMultiSQL($sql, $eventname) to execute multiple SQL queries&lt;br /&gt;
* fetchFieldStruct($id=null, $nohide=false, $table=null, $cachepath=null) allows you to fetch the field&#039;s SQL structure of not only custom fields, but of any database&#039;s column using the $table variable. Eg: fetchFieldStruct(&#039;mycolumn&#039;, true, &#039;mytable&#039;);&lt;br /&gt;
* probeTable($table) helps you to check if a table exists in the database&lt;br /&gt;
* fetch_object($resource) will return a PHP object from a $resource, similarly to mysql_fetch_object() but with two advantages: it works on several DBMS (not only mysql), and it always return lowercase properties names (else depending on the configuration of the DBMS, the request can return uppercase or mixed case properties names).&lt;br /&gt;
* escape() and reverse_escape() are similare to mysql_real_escape but is reversible and DBMS-independent (so you can use it with other DBMSes).&lt;br /&gt;
&lt;br /&gt;
== Managing CustomFields structure ==&lt;br /&gt;
&lt;br /&gt;
The first step into CustomFields is to create and manage the structure of a custom field.&lt;br /&gt;
&lt;br /&gt;
=== Creating a custom field definition ===&lt;br /&gt;
&lt;br /&gt;
To create a custom field, you can use the addCustomField() method.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;addCustomField($fieldname, $type, $size, $nulloption, $defaultvalue = null, $constraint = null, $customtype = null, $customdef = null, $customsql = null, $fieldid = null, $extra = null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will take care of forging the (complicated) SQL query with the forgeSQLCustomField() method, and will return you an error if there was a problem.&lt;br /&gt;
&lt;br /&gt;
=== Updating a custom field definition ===&lt;br /&gt;
&lt;br /&gt;
To update a custom field, you can use the updateCustomField() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;&lt;br /&gt;
updateCustomField($fieldid, $fieldname, $type, $size, $nulloption, $defaultvalue, $constraint = null, $customtype = null, $customdef = null, $customsql = null, $extra = null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: the updateCustomField() and addCustomField() methods can both be used since updateCustomField() is in fact an alias for addCustomField(). CustomFields will automatically detect if a custom field already exists, and update it if that&#039;s true, or create it.&lt;br /&gt;
&lt;br /&gt;
Note2: updating a custom field will automatically take care of the constraints and foreign keys on its own, thus you can change the foreign key, or remove it altogether if set to null.&lt;br /&gt;
&lt;br /&gt;
=== Delete a custom field definition ===&lt;br /&gt;
&lt;br /&gt;
You can easily delete a custom field by column_name or by ordinal_position:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;deleteCustomField($id);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: constraints and foreign keys will also be deleted automatically so you shouldn&#039;t encounter any error.&lt;br /&gt;
&lt;br /&gt;
=== Fetch one custom field&#039;s SQL structure ===&lt;br /&gt;
&lt;br /&gt;
To get the SQL structure of a custom field, which can be quite useful to know for example its datatype or if it has a foreign key, or accepts null values, you can use the fetchFieldStruct() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
fields = $customfields-&amp;gt;fetchFieldStruct($id=null, $nohide=false, $table=null, $cachepath=null); // where $id can be a column_name, an ordinal_position from information_schema.columns or null if you want to fetch all the columns at once&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method will either return null, either a single field object, either an array on field objects.&lt;br /&gt;
&lt;br /&gt;
Note: this function will use caching by default to accelerate future results, but you can disable it, or even change the default cache path. For more informations please read the comments in the source code.&lt;br /&gt;
&lt;br /&gt;
=== Fetch several custom fields&#039; SQL structures ===&lt;br /&gt;
&lt;br /&gt;
Use the fetchAllFieldsStruct() method, which is an alias for fetchFieldStruct():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;fetchAllFieldsStruct($nohide=false, $table=null, $cachepath=null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: this method will always return an array of fields objects, even if only one is found (for more consistency), and this is the main difference with fetchFieldStruct(null).&lt;br /&gt;
&lt;br /&gt;
== Managing custom fields records ==&lt;br /&gt;
&lt;br /&gt;
Now that you know how to create and manage the structure of custom fields, we can proceed on to managing the records.&lt;br /&gt;
&lt;br /&gt;
=== Creating a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Simply use the create() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;create($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
$object must be an object containing as its properties the customfields data.&lt;br /&gt;
&lt;br /&gt;
For example, if you have a field named mycustomfield, your object must have $object-&amp;gt;mycustomfields defined.&lt;br /&gt;
&lt;br /&gt;
This method will store in the database ALL the custom fields that were created/edited and with a valid value (eg: an empty value for a field that does not accept null value is invalid) AT ONCE! So you can modify all the columns of one record at once, but not several records at once.&lt;br /&gt;
&lt;br /&gt;
Concretely, this is very useful with Dolibarr, because modules tends to store everything inside a single object generally called $object, and CustomFields tries to hook onto $object, so that you can just pass $object as is to create() and it will save all the custom fields.&lt;br /&gt;
&lt;br /&gt;
But you can also create an empty object with $object = new stdClass(); and then manually fill the custom fields records with whatever you want, and then call the create() method, this will work as well!&lt;br /&gt;
&lt;br /&gt;
You can also just modify only one column of one record, by just specifying one custom field column_name in your $object. The other custom fields that are not specified won&#039;t be created/modified.&lt;br /&gt;
&lt;br /&gt;
=== Updating a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Use the update() method, which is an alias for the create() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;update($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As noted above, update() is an alias of the create() method, which means that CustomFields will try to detect by itself if a record needs to be updated or created.&lt;br /&gt;
&lt;br /&gt;
=== Deleting a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Use the delete() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;delete($id); // where $id is = to fk_moduleid column, NOT rowid&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fetching a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Just use the fetch() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$records = $customfields-&amp;gt;fetch($id=null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where $id can either be = fk_moduleid (NOT rowid), or null to fetch all the records at once.&lt;br /&gt;
&lt;br /&gt;
This method will either return null, a single record object, or an array of records objects.&lt;br /&gt;
&lt;br /&gt;
=== Fetching all custom fields&#039; records ===&lt;br /&gt;
&lt;br /&gt;
Use the fetchAll() method, which is an alias for fetch():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$records = $customfields-&amp;gt;fetchAll();&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: this method will always return an array of records object, for more consistency (and this is the main difference with fetch(null) ).&lt;br /&gt;
&lt;br /&gt;
=== Fetching referenced records of a constrained field ===&lt;br /&gt;
&lt;br /&gt;
A constrained custom field points to another database table, and a value for this constrained field is the rowid of a record in the referenced table.&lt;br /&gt;
&lt;br /&gt;
Thus, the principal purpose of a constrained custom field is not the value of the field in itself (it&#039;s simply an integer), but rather the other fields available from the referenced table, the constrained field being in fact just a pointer.&lt;br /&gt;
&lt;br /&gt;
To fetch the remote record from the referenced table, use the fetchReferencedValuesList() of the CustomFields class:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$fkrecord = fetchReferencedValuesList($field, $id=null, $where=null, $allcolumns=false);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
&lt;br /&gt;
* $field is a custom field object (fetched using fetchFieldStruct() or fetchAllFieldsStruct()),&lt;br /&gt;
* $id is the rowid of the record to get, just like fetch() and fetchAll(). Can be null to fetch all remote records (or to use the $where clause).&lt;br /&gt;
* $where is a string without the WHERE clause (eg: &amp;quot;fk_facture=1&amp;quot;) to get finer control. In this case, you can use $id or set $id=null depending on your purpose. This is mainly a private argument to allow for cascade option.&lt;br /&gt;
* $allcolumns if false, it will fetch only the required columns to do the smart value substitution. If true, it will fetch all remote columns (useful for ODT substitution). If an array of strings, each string will be a column to fetch from the remote database.&lt;br /&gt;
&lt;br /&gt;
Note that this function is NOT recursive (it will fetch the records from the referenced table, and that&#039;s all, it won&#039;t fetch the custom fields of the referenced table nor recursively fetch the remote constrained custom fields). To do recursive fetching, see in the Facade API.&lt;br /&gt;
&lt;br /&gt;
Note2: this function automatically manages Smart Value Substitution depending of the $field-&amp;gt;column_name.&lt;br /&gt;
&lt;br /&gt;
=== Recursive fetching referenced records of a constrained field ===&lt;br /&gt;
&lt;br /&gt;
If you want to recursively fetch a constrained field (eg: constrainedfield1-&amp;gt;ref_table-&amp;gt;constrainedfield2-&amp;gt;ref_table2-&amp;gt;...), you need another function called fetchReferencedValuesRec(). Technically, this function is in the Facade API (inside /customfields/lib/customfields_aux.lib.php), but it isn&#039;t meant to be used by lambda users (contrary to customfields_fill_object()), but is mainly a helper function for the rest of the facade API.&lt;br /&gt;
&lt;br /&gt;
fetchReferencedValuesRec() works very similarly to fetchReferencedValuesList(), but it will work recursively.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$fkrecord = fetchReferencedValuesRec($customfields, $field, $id, $recursive=true)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where $customfields is the CustomFields instance for the module you are working on, $field is the constrained field you want to fetch, $id is the record&#039;s id you want to fetch, and $recursive enables or disables the recursion.&lt;br /&gt;
&lt;br /&gt;
Note that this function automatically manages Smart Value Substitution (and also recursively).&lt;br /&gt;
&lt;br /&gt;
Note2: This function tries to automatically avoid an infinite recursion loop by using a $blacklist: for a given constrained custom field, a table won&#039;t be visited twice. This means that for example you can do: invoice-&amp;gt;user-&amp;gt;socpeople-&amp;gt;invoice, this will work and every referenced fields will be fetched, but it will automatically stop the recursion at the second invoice, because else it could continue in a loop to user, socpeople, invoice, user, socpeople, etc.&lt;br /&gt;
&lt;br /&gt;
Note3: when adding recursively referenced fields, the key in the returned array will be prefixed by the name of the custom field that led to those remote fields. Thus, you can have something like &amp;quot;cf_myfield1_cf_remotefield1_cf_remotefield2&amp;quot; if you have a constrained field cf_myfield1-&amp;gt;cf_remotefield1-&amp;gt;cf_remotefield2.&lt;br /&gt;
&lt;br /&gt;
== Input forms for custom fields ==&lt;br /&gt;
&lt;br /&gt;
The API provides some neat ways to automatically manage the input forms for users to input new values, with the best adapted HTML form based on the custom field&#039;s data type.&lt;br /&gt;
&lt;br /&gt;
The following methods will print input fields that are best adapted to the SQL data type of your custom fields as well as figure out the possible values and even format them (for example: a not null enum will be printed as a HTML select list without an empty option; fetch automatically remote columns for constrained fields and show them in a select).&lt;br /&gt;
&lt;br /&gt;
So you don&#039;t have to bother how your data will be presented to the user nor how it will be processed.&lt;br /&gt;
&lt;br /&gt;
=== Printing an input form adapted to the field&#039;s data type ===&lt;br /&gt;
&lt;br /&gt;
showInputField() is a sort of converter customfieldstructure -&amp;gt; HTML input field.&lt;br /&gt;
&lt;br /&gt;
Use the &#039;&#039;&#039;showInputField()&#039;&#039;&#039; method to print the right input form for a custom field.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;showInputField($field, $currentvalue=null, $moreparam=&#039;&#039;, $ajax_php_callback=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
showInputField() takes 4 parameters:&lt;br /&gt;
&lt;br /&gt;
* $field is a CustomFields structure object (eg: returned by fetchFieldStruct()) and is necessary to detect the right datatype and the possible values.&lt;br /&gt;
* $currentvalue is the current value if you want to preselect a value (for example if the user already defined a value, you can reload it here). This is optional, in case it is not defined, either a null/empty value will be chosen, or either the default value defined when creating the custom field.&lt;br /&gt;
* $moreparam is used to add more HTML attributes, if you want for example to add some css styling or whatever you want.&lt;br /&gt;
* $ajax_php_callback is optional and allows to specify the relative url to the php callback script. If specified, an AJAX script will be automatically attached to this field (see AJAX-PHP callback below).&lt;br /&gt;
&lt;br /&gt;
showInputField() only prints ONE HTML input field, thus you have to enclose it/them by your own HTML form.&lt;br /&gt;
&lt;br /&gt;
eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print &#039;&amp;lt;form method=&amp;quot;get&amp;quot; action=&amp;quot;?&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
print &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;customtoken&amp;quot; value=&amp;quot;ABCDEFGH&amp;quot; /&amp;gt;&#039;; // example of a custom hidden HTML input field, that can be used to pass a token for example&lt;br /&gt;
// Loop over each custom fields (you have to load them before with $customfields-&amp;gt;fetchAllFieldsStruct()&lt;br /&gt;
foreach ($customfields-&amp;gt;fields as $field) {&lt;br /&gt;
    print $customfields-&amp;gt;showInputField($field, $currentvalue=null, $moreparam=&#039;&#039;);&lt;br /&gt;
}&lt;br /&gt;
print &#039;&amp;lt;/form&amp;gt;&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can also store the resulting HTML input field and process it by a regexp for example:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$myhtmlfield = $customfields-&amp;gt;showInputField($field, $currentvalue=null, $moreparam=&#039;&#039;);&lt;br /&gt;
preg_replace($myhtmlfield, ..., ...);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: for constrained fields, if the name follows the convention of &#039;&#039;&#039;Smart Value Substitution&#039;&#039;&#039; (ie: remote columns names are included in the custom field&#039;s name), showInputField() will &#039;&#039;&#039;automatically fetch the remote values, format them, and present them&#039;&#039;&#039; in a HTML select.&lt;br /&gt;
&lt;br /&gt;
=== Printing an input form for a custom field ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;showInputForm()&#039;&#039;&#039; is a function used to print a single (and only one!) custom field with an adapted HTML input field, but enclosed by a HTML form. This is used for example when you want to allow to edit a single custom field but not the others.&lt;br /&gt;
&lt;br /&gt;
showInputForm() is in fact a wrapper for showInputField(), thus it will call showInputField() and simply enclose it by a form.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;showInputForm($id, $field, $currentvalue=null, $idvar=&#039;id&#039;, $page=null, $moreparam=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== AJAX-PHP callback ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;showInputFieldAjax()&#039;&#039;&#039; will output an AJAX script (using jQuery) to send and receive data to and from a specified php script. The AJAX script will automatically manage HTML form input&#039;s updating as long as the received data follows some convention.&lt;br /&gt;
&lt;br /&gt;
An AJAX script can be attached to any field (including non-CustomFields) by using the method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;showInputFieldAjax($id, $php_callback_url, $on_func=&amp;quot;change&amp;quot;, $request_type=&amp;quot;post&amp;quot;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
* $id is the HTML id of the field you want to attach the AJAX to.&lt;br /&gt;
* $phpcallback the relative path from Dolibarr&#039;s htdocs root folder to the php script that will receive and send back data.&lt;br /&gt;
* $on_func is the Javascript or jQuery event that should trigger the AJAX script (by default on change).&lt;br /&gt;
* $request_type is the HTML query type that will be used to send the data to the PHP callback (either &amp;quot;get&amp;quot; or &amp;quot;post&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
The AJAX script will automatically manage sending/receiving of data:&lt;br /&gt;
&lt;br /&gt;
* automatically send all form&#039;s inputs values, as well as the current calling field&#039;s name and value. The sending happens on change by default, but another event can be set using the $on_event argument. Data will be sent using standard GET or POST.&lt;br /&gt;
* Upon reception of data from the php script, the AJAX script will automatically parse the data and update the HTML fields.&lt;br /&gt;
&lt;br /&gt;
The PHP callback script must send back to AJAX the data in JSON encoded format, and with with a specific format:&lt;br /&gt;
&lt;br /&gt;
* the data must be an associative array, where each entry&#039;s key is the name (not ID!) of an HTML field to update.&lt;br /&gt;
* each entry&#039;s value is an associative array, where each sub-entry&#039;s key is the action to do. Available actions: &#039;options&#039;, &#039;value&#039;, &#039;html&#039;, &#039;alert&#039;.&lt;br /&gt;
* each sub-entry&#039;s value is the value for this action and HTML field.&lt;br /&gt;
&lt;br /&gt;
Thus, this approach allows to generically support a wide range of updating actions, managed automatically.&lt;br /&gt;
&lt;br /&gt;
You, the implementer, have just to edit the PHP callback script and return an associative array defining what action you want for each field you want to update. This is exactly what has been implemented in the Custom AJAX Functions library, which simplifies even more your job by managing automatically the step described here (generating AJAX and formatting data), so that you can focus on &#039;&#039;&#039;making the data&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Printing custom fields in documents ==&lt;br /&gt;
&lt;br /&gt;
The API can also automatically manage the printing and formatting of custom fields in your ODT, PDF and HTML documents.&lt;br /&gt;
&lt;br /&gt;
=== Printing in HTML ===&lt;br /&gt;
&lt;br /&gt;
You can either just print the value as-is, or pass them through the printField() or simpleprintField() function to format them (very useful for constrained fields) - see below.&lt;br /&gt;
&lt;br /&gt;
=== Printing in documents ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;printField()&#039;&#039;&#039; is used to properly print and translate a custom field&#039;s record&#039;s value. Generally, you can just print the custom field&#039;s value as-is, but in a few cases (eg for boolean types, or constrained fields), using the printField() method will more properly print the value.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;printField($field, $value, $outputlangs=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
* $field is the custom field&#039;s SQL structure as an object (eg: returned by fetchFieldStruct()).&lt;br /&gt;
* $value is the custom field&#039;s value for the current record (eg: $customfields-&amp;gt;fields-&amp;gt;myfield).&lt;br /&gt;
* $outputlangs is the Translate object for the target language.&lt;br /&gt;
&lt;br /&gt;
For example, with a constrained field with Smart Value Substitution, instead of printing the id like 1, it will print the full name of user like FirstName Name.&lt;br /&gt;
&lt;br /&gt;
This method will also translate a value if a translation is found in the language files.&lt;br /&gt;
&lt;br /&gt;
Another method &#039;&#039;&#039;printFieldPDF()&#039;&#039;&#039; can be used to properly print a value for a PDF (properly encoding the characters).&lt;br /&gt;
&lt;br /&gt;
Powertip: this function can also be used to get the Smart Value Substituted value of the field (instead of printing the rowid) in the field is constrained.&lt;br /&gt;
&lt;br /&gt;
=== Simple printing in documents ===&lt;br /&gt;
&lt;br /&gt;
If you don&#039;t want to have to manually fetch the structure of every custom field whose you want to print the value, you can then use a simplifier method called &#039;&#039;&#039;simpleprintField()&#039;&#039;&#039; which will take as input a custom field name instead of a custom field SQL structure object.&lt;br /&gt;
&lt;br /&gt;
This method is just a simplifier (Facade) wrapper for the printField() method, and thus will have the same advantages (fetch constrained fields, values translation, etc.).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;simpleprintField($fieldname, $value, $outputlangs=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
* $fieldname is the custom field&#039;s name (eg: &amp;quot;myfield&amp;quot;).&lt;br /&gt;
* $value is the custom field&#039;s value for the current record (eg: $customfields-&amp;gt;fields-&amp;gt;myfield).&lt;br /&gt;
* $outputlangs is the Translate object for the target language.&lt;br /&gt;
&lt;br /&gt;
Another method &#039;&#039;&#039;simpleprintFieldPDF()&#039;&#039;&#039; can be used to properly print a value for a PDF (properly encoding the characters).&lt;br /&gt;
&lt;br /&gt;
Powertip: this function can also be used to get the Smart Value Substituted value of the field (instead of printing the rowid) in the field is constrained.&lt;br /&gt;
&lt;br /&gt;
=== Find the label ===&lt;br /&gt;
&lt;br /&gt;
Finding the label of a custom field might not be as simple as it seems. First, values of properties objects may be easily used, but their name (key) is a worst job. Secondly, custom fields&#039; labels can be translated, and the following functions are expressly made for that purpose. Thirdly, for PDF and some other documents you have to encode the characters if you want them to properly be drawn.&lt;br /&gt;
&lt;br /&gt;
So if you want to print a custom field&#039;s label with translation (multilingual) support, just use the &#039;&#039;&#039;findLabel()&#039;&#039;&#039; method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$translatedfieldname = $customfields-&amp;gt;findLabel($fieldname, $outputlangs = &#039;&#039;); // where $fieldname is just a custom field label (eg: cf_mycustomfield or mycustomfield, both will work) and $outputlangs is the target language object (see the Translate class native of Dolibarr)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As for the other printing methods, you also have the &#039;&#039;&#039;findLabelPDF()&#039;&#039;&#039; wrapper method to easily print labels that with characters encoded for PDF.&lt;br /&gt;
&lt;br /&gt;
= Facade API =&lt;br /&gt;
&lt;br /&gt;
The Facade API was made to ease the usage of the CustomFields class with the Dolibarr system. It provides a neat way to do many operations with single commands, and will automatically manage the loading of the CustomFields class, and its usage with many optimizations.&lt;br /&gt;
&lt;br /&gt;
== customfields_fill_object() ==&lt;br /&gt;
&lt;br /&gt;
You can load the Facade API and then use the &#039;&#039;&#039;customfields_fill_object()&#039;&#039;&#039; to automatically fetch the fields structures and the records, and also automatically link to remote fields when a constrained field is found. This is an all-in-one solution to easily fetch CustomFields datas, and is also optimized to fully use caching and other special properties of the CustomFields class.&lt;br /&gt;
&lt;br /&gt;
Concretely, you just have to include the Facade API, then pass the customfields_fill_object() with a Dolibarr $object, and the Facade API will return a CustomFields object with appended the custom fields records and also the custom fields&#039; SQL structures.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$customfields = customfields_fill_object($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* $object will then include the custom fields records associated with this object (eg: $object-&amp;gt;customfields-&amp;gt;cf_yourfield).&lt;br /&gt;
* $customfields will be a CustomFields object containing the fields structures (eg: $customfields-&amp;gt;fields-&amp;gt;yourfield).&lt;br /&gt;
&lt;br /&gt;
You can also call any CustomFields function if you need to using the $customfields object, eg: $customfields-&amp;gt;simpleprintField($object-&amp;gt;customfields-&amp;gt;cf_yourfield);&lt;br /&gt;
&lt;br /&gt;
Note: $object can also be a dummy object containing only two properties that are required: $object-&amp;gt;id and $object-&amp;gt;table_element.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will now describe the full specification of this function:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$customfields = customfields_fill_object(&amp;amp;$object,$fromobject = null, $outputlangs = null, $prefix = null,$pdfformat = false);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* $object is the object where the customfields will be saved&lt;br /&gt;
* $fromobject is the object from where the customfields will be fetched (this can be a different module than $object, or can be null to be the same as $object)&lt;br /&gt;
* $outputlangs is the target language for translation of fields value (if empty, the default current global translation object will be used)&lt;br /&gt;
* $prefix allows you to save the custom fields in a sub-object. By default, when $prefix=null, custom fields are saved in the path $object-&amp;gt;customfields, but when you specify a prefix, custom fields will be saved inside $object-&amp;gt;customfields-&amp;gt;$prefix. This may be useful if you want to store multiple object&#039;s custom fields or records in the same $object.&lt;br /&gt;
* $pdfformat defines if you want a special formatting for the values: if null, there&#039;s no formatting, if false, there is a simple formatting (eg: translation of fields&#039; values), if true then the formatting will be adapted for PDF templating.&lt;br /&gt;
&lt;br /&gt;
For more informations on pratical usages of this function, please [[Module_CustomFields#Implementing_in_php_code_.28dolibarr_core_modules_or_your_own_module.29|read the user documentation on how to implement CustomFields in php code]] or [[Module_CustomFields_Cases#Linking_Dolibarr_objects_from_two_different_modules|read an example case of linking two objects of different modules together]].&lt;br /&gt;
&lt;br /&gt;
== customfields_fill_object_lines() ==&lt;br /&gt;
&lt;br /&gt;
Similar to customfields_fill_object() for normal custom fields, there is also a &#039;&#039;&#039;customfields_fill_object_lines()&#039;&#039;&#039; to fetch custom fields associated with lines of products/services. This function is also optimized and it is strongly advised to use it and to not try to fetch manually this kind of custom fields (ie: associated with lines of products/services rather than objects), because there are a lot of optimizations going on behind the scene and that are very hard to do manually.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields for lines of products/services (you can then access them by doing $object-&amp;gt;customfields-&amp;gt;lines-&amp;gt;$lineid-&amp;gt;cf_yourfield)&lt;br /&gt;
$customfields = customfields_fill_object_lines($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Requirements: $object can be a dummy object but must contain at least 2 properties: $object-&amp;gt;id and $object-&amp;gt;table_element_line.&lt;br /&gt;
&lt;br /&gt;
For more informations on pratical usages of this function, please [[Module_CustomFields#Implementing_in_php_code_.28dolibarr_core_modules_or_your_own_module.29|read the user documentation on how to implement CustomFields in php code]].&lt;br /&gt;
&lt;br /&gt;
= Architecture =&lt;br /&gt;
Here is a full list of the CustomFields packaged files with a short description (for a more in-depth view just crawl the source files, they are full of comments):&lt;br /&gt;
&lt;br /&gt;
== Core files ==&lt;br /&gt;
files that are necessary for the CustomFields to work, they contains the core functions&lt;br /&gt;
&lt;br /&gt;
* /customfields/admin/customfields.php --- Administrator&#039;s configuration panel : this is where you create and manage the custom fields definitions&lt;br /&gt;
* /customfields/class/actions_customfields.class.php --- Hooks class : used to hook into Dolibarr core modules without altering any core file (can be used to hook into your own modules too). Also used to preprocess the parameters before calling the printforms library.&lt;br /&gt;
* /customfields/class/customfields.class.php --- Core class : every database action is made here in this class. You can find some printing functions because they are very generic.&lt;br /&gt;
* /customfields/conf/conf_customfields.lib.php --- Configuration file : contains the main configurable variables to adapt CustomFields to your needs or to expand its support to other modules and more native sql types.&lt;br /&gt;
* /customfields/conf/conf_customfields_func.lib.php --- Auxiliary functions library to read and manage config variables&lt;br /&gt;
* /customfields/core/modules/modCustomFields.class.php --- Dolibarr&#039;s module definition file : this is a core file necessary for Dolibarr to recognize the module and to declare the hooks to Dolibarr (but it does not store anything else than meta-informations).&lt;br /&gt;
* /customfields/core/substitutions/functions_customfields.lib.php --- CustomFields substitution class for ODT generation : necessary to support customfields tags in ODT files&lt;br /&gt;
* /customfields/core/triggers/interface_50_modCustomFields_SaveFields.class.php --- Core triggers file : this is where the actions on records are managed. This is an interface between other modules and CustomFields management. You should not modify this file but you should add your triggers in $triggersarray inside conf_customfields.lib.php, but if you really have a special need and know what you do, you can edit this file to directly add your own triggers. Also, if CustomFields are shown in the forms but cannot be modified nor saved, then probably this trigger file is not detected by Dolibarr (maybe you can try to lower the number 50 to raise the priority?).&lt;br /&gt;
* /customfields/fields/customfields_fields_extend.lib.php.example --- Overloading functions library with users functions: this is where implementers can use their own php code to manage the custom fields they want (one function per action and per field).&lt;br /&gt;
* /customfields/fields/customfields_fields_ajax_custom.lib.php.example --- Custom AJAX functions library with users functions: this is where implementers can use their own php code to manage the custom fields cascading and AJAX calls, similarly to the Overloading functions library (one function per action and per field).&lt;br /&gt;
* /customfields/langs/code_CODE/customfields.lang --- Core language file : this is where you can translate the admin config panel (data types names, labels, descriptions, etc.)&lt;br /&gt;
* /customfields/langs/code_CODE/customfields-user.lang --- User defined language file : this is where you can store the labels and values of your custom fields (see the related chapter)&lt;br /&gt;
* /customfields/lib/customfields_aux.lib.php --- Simplifier library to easily use CustomFields class to populate an $object with custom fields datas (Facade design pattern). This is what you should use for most of your needs.&lt;br /&gt;
* /customfields/lib/customfields_printforms.lib.php --- Core printing library for records : contains only printing functions, there&#039;s not really any core functions inside but it allows to manage the printing of the custom fields records and their editing.&lt;br /&gt;
* /customfields/lib/customfields_ajax_wrapper.lib.php --- AJAX wrapper library to automatically manage cascading at objects creation. This isn&#039;t really necessary for CustomFields to function, this is more an addition to allow dynamical AJAX updating, but CustomFields core functionalities can work without this library.&lt;br /&gt;
* /customfields/sql/*.sql --- Unused (the tables are created directly via a function in the customfields.class.php) - but the sql files are still valid, they were used as squeletton in the first designs.&lt;br /&gt;
&lt;br /&gt;
== PDFTest module ==&lt;br /&gt;
optional module to append a page listing all custom fields values on newly generated PDF documents. To be used to test if custom fields are working correctly before making one&#039;s own template.&lt;br /&gt;
&lt;br /&gt;
* /customfieldspdftest/class/actions_customfieldspdftest.class.php --- Main functions library and hooks class, to hook onto pdf generation. Contains the PDFTest functions.&lt;br /&gt;
* /customfieldspdftest/core/modules/modCustomFieldsPDFTest.class.php --- Module descriptor for Dolibarr&lt;br /&gt;
&lt;br /&gt;
== Runtime outline ==&lt;br /&gt;
&lt;br /&gt;
Here is an outline of how CustomFields works: what functions will be called and why:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At admin page:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* No external function call (apart for some icons or needed admin functions like for tabs), everything that is done are sql queries via CustomFields class (to fetch and modify the sql columns in the customfields tables).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At printing:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Module&#039;s hook formObjectOptions calls Dolibarr&#039;s hookmanager&lt;br /&gt;
* Hookmanager calls CustomFields /class/actions_customfields.class.php, function formObjectOptions() of CustomFields&lt;br /&gt;
* Preprocess a few parameters (mainly the $action) and calls customfields_print_forms()&lt;br /&gt;
* customfields_print_forms() preprocess all the hook&#039;s parameters and assimilate them with the CustomFields&#039;s configuration parameters (there&#039;s no connection to the database at this stage, only parameters preprocessing), then calls /lib/customfields_printforms.lib.php, functions customfields_print_creation_form() or customfields_print_datasheet_form&lt;br /&gt;
* both customfields_print_creation_form() lookup the database using CustomFields class and prints empty custom fields; customfields_print_datasheet_form() lookup the database using CustomFields class, fetch the values of the custom fields and prints them;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At edition:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Same steps as &#039;&#039;&#039;At printing&#039;&#039;&#039;&lt;br /&gt;
* then customfields_print_datasheet_form() process the custom field being edited differently than the others: if editing, it prints the html input field (to enter a new value), or if saving it just saves the data into the database by calling the CUSTOMFIELDS_MODIFY trigger and then continue to print the other fields normally.&lt;br /&gt;
* CUSTOMFIELDS_MODIFY trigger calls $customfields-&amp;gt;update($newrecord);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At creation (saving):&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Module&#039;s creation trigger MYMODULE_CREATE calls Dolibarr&#039;s run_trigger()&lt;br /&gt;
* Dolibarr run_trigger() calls CustomFields trigger file /core/trigger/interface_50_modCustomFields_SaveFields.class.php, function run_trigger()&lt;br /&gt;
* CustomFields&#039;s run_trigger() assimilate the given parameters with the CustomFields&#039;s config parameters, and if the trigger is found in $triggersarray, then the appropriate action is taken (by default: _CREATE or another standard action if recognized), if not in $triggersarray, the function will still try to automatically detect the appropriate action by detecting MYMODULE_ALISTOFACTIONS with a regular expression.&lt;br /&gt;
* The appropriate action is then done (creation in this case) by calling $customfields-&amp;gt;create($record).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At edition or creation of product lines:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Similar to &#039;&#039;&#039;At edition&#039;&#039;&#039; but with hooks: formCreateProductOptions and formEditProductOptions.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At ODT document generation and substitution:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Module&#039;s ODT generator calls for Dolibarr&#039;s function complete_substitution_array(), which then calls for CustomFields substitution file /core/substitution/functions_customfields.lib.php, function customfields_completesubstitutionarray()&lt;br /&gt;
* CustomFields&#039;s function customfields_completesubstitutionarray() calls the simplifier library customfields_aux.lib.php, function customfields_fill_object() and make the substitution inside the $substitutionarray.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At ODT document generation and substitution for product lines:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Same as the previous point but this function is called for every product lines, and thus the data is cached. Also the simplifier function is here called customfields_fill_object_lines(), and the CustomFields&#039;s substitution function called customfields_completesubstitutionarray_lines().&lt;br /&gt;
&lt;br /&gt;
= How to add the support of a new module =&lt;br /&gt;
&lt;br /&gt;
There are two cases: either the module already implements hooks, and you just have to configure CustomFields to support it, or either the module does not implements hooks nor triggers.&lt;br /&gt;
&lt;br /&gt;
This chapter will guide you through the steps to make any module supported by CustomFields (be it a Dolibarr core module or your own module).&lt;br /&gt;
&lt;br /&gt;
== Preliminary test ==&lt;br /&gt;
&lt;br /&gt;
This preliminary test will allow you to know at a glance how much work needs to be done in order to make your module supported by CustomFields, and it will also give you all the config parameters if they are available.&lt;br /&gt;
&lt;br /&gt;
Important note: this test will only give data about hooks, which is the main part to make CustomFields work for your module. But for triggers, there is no way to test, so you will have to check the triggers manually (either by testing if the custom fields are saved at creation page, or by looking at the source code of the objects classes).&lt;br /&gt;
&lt;br /&gt;
Edit the php script of the module (check the URL when you access the module&#039;s datasheet or creation page - attention both are two different pages, so you have to do this twice), then put this at the very &#039;&#039;&#039;end&#039;&#039;&#039; of the php script:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// CustomFields debuglines to show config parameters for current module&lt;br /&gt;
print(&amp;quot;&amp;lt;pre&amp;gt;&amp;quot;); // html nice output&lt;br /&gt;
print(&amp;quot;table_element: {$object-&amp;gt;table_element} - table_element_line: {$object-&amp;gt;table_element_line} - contexts: &amp;quot;);&lt;br /&gt;
print_r($hookmanager-&amp;gt;contextarray);&lt;br /&gt;
print(&amp;quot; - conf hooks_modules: &amp;quot;);&lt;br /&gt;
print_r($conf-&amp;gt;hooks_modules);&lt;br /&gt;
print(&amp;quot; - hooks attached: &amp;quot;);&lt;br /&gt;
print_r($hookmanager-&amp;gt;hooks);&lt;br /&gt;
print(&amp;quot; - user rights: &amp;quot;);&lt;br /&gt;
print_r($user-&amp;gt;rights);&lt;br /&gt;
print(&amp;quot;&amp;lt;/pre&amp;gt;&amp;quot;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now reload the page, and scroll at the very bottom. You should see some informations about the module.&lt;br /&gt;
&lt;br /&gt;
Depending on the data available, you will know what you have to do:&lt;br /&gt;
&lt;br /&gt;
* If all the datas is available, then it&#039;s ok, jump to [[Module_CustomFields_Dev#Configuring_CustomFields_to_support_the_module]] and copy these datas to the parameters of conf_customfields.lib.php&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;table_element&#039;&#039;&#039;: $object is not defined, try to find how is called the main object variable and replace $object by this variable in the debug code given above (eg: in Agenda, the object variable is called $agenda and $agendacomm).&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;table_element_line&#039;&#039;&#039;: either there&#039;s no products lines for this module, either the main object variable is not called $object (see the previous point). One last case is that the object is not standard and does not store table_element_line (the name of the table that stores its products lines), in which case you have to manually add it in the class (eg: $this-&amp;gt;table_element_line = &#039;something&#039;).&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;contexts&#039;&#039;&#039;: the hookmanager is not defined, and probably there&#039;s no hook at all in this module, so you&#039;ll have to implement them by yourself. Jump to [[Module_CustomFields_Dev#Hooks]].&lt;br /&gt;
&lt;br /&gt;
* If there is a list of &#039;&#039;&#039;contexts&#039;&#039;&#039; but the module cannot be found in the list: the hookmanager is defined but the module has not defined a context, you still have to implement the hooks. Jump to [[Module_CustomFields_Dev#Hooks]].&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;conf hooks_modules&#039;&#039;&#039;: this variable is not necessary, empty or not it doesn&#039;t matter. But if you see customfields and/or customfieldspdftest inside the list, it is a confirmation that CustomFields indeed is hooking the module correctly.&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;hooks attached&#039;&#039;&#039;, or CustomFields cannot be found in the list: CustomFields has not been properly configured to hook to this module, or you forgot to disable then re enable CustomFields in order to update the CustomFields&#039;s hooks. Jump to [[Module_CustomFields_Dev#Configuring_CustomFields_to_support_the_module]].&lt;br /&gt;
&lt;br /&gt;
* If there is no &#039;&#039;&#039;$user-&amp;gt;rights&#039;&#039;&#039;: there is either no standard security implemented in this module, or either the variable is called with another name. If there is no security, you don&#039;t need to configure it in CustomFields, but if there is a security, you need to find how the variable is called (like $object can get another name sometimes).&lt;br /&gt;
&lt;br /&gt;
* If there is &#039;&#039;&#039;$user-&amp;gt;rights&#039;&#039;&#039; but the module can&#039;t be found in the list: either the name for the rights is different (this often happens, the rights array is not yet standardized), or either the rights are hardcoded into the module&#039;s code (rights should be announced to the $user-&amp;gt;rights array, but sometimes it happens that the dev forgot to do so, and thus the right is entirely hardcoded and you can&#039;t find it in the dynamic rights array). In any of these two cases, you will have to manually read the module&#039;s code to find the right. Also, maybe there is no security implemented, but since $user-&amp;gt;rights is declared this is less probable.&lt;br /&gt;
&lt;br /&gt;
Here is an example of output on a correctly hooked module (invoice):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
table_element: facture - table_element_line: facturedet - contexts: Array&lt;br /&gt;
(&lt;br /&gt;
    [0] =&amp;gt; searchform&lt;br /&gt;
    [1] =&amp;gt; leftblock&lt;br /&gt;
    [2] =&amp;gt; toprightmenu&lt;br /&gt;
    [3] =&amp;gt; invoicecard&lt;br /&gt;
)&lt;br /&gt;
 - conf hooks_modules: Array&lt;br /&gt;
(&lt;br /&gt;
    [customfields] =&amp;gt; invoicecard:propalcard:productcard:ordercard:thirdpartycard:contactcard:ordersuppliercard:invoicesuppliercard:membercard:actioncard:projectcard:projecttaskcard:contractcard:interventioncard:doncard:tripsandexpensescard:taxvatcard:expeditioncard:invoicecard:propalcard:ordercard:ordersuppliercard:invoicesuppliercard&lt;br /&gt;
    [customfieldspdftest] =&amp;gt; Array&lt;br /&gt;
        (&lt;br /&gt;
            [0] =&amp;gt; pdfgeneration&lt;br /&gt;
            [1] =&amp;gt; ordersuppliercard&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
)&lt;br /&gt;
 - hooks attached: Array&lt;br /&gt;
(&lt;br /&gt;
    [invoicecard] =&amp;gt; Array&lt;br /&gt;
        (&lt;br /&gt;
            [customfields] =&amp;gt; ActionsCustomFields Object&lt;br /&gt;
                (&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
)&lt;br /&gt;
 - user rights: stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [user] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [user] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [lire] =&amp;gt; 1&lt;br /&gt;
                    [creer] =&amp;gt; 1&lt;br /&gt;
                    [password] =&amp;gt; 1&lt;br /&gt;
                    [supprimer] =&amp;gt; 1&lt;br /&gt;
                    [export] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [self] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [creer] =&amp;gt; 1&lt;br /&gt;
                    [password] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [user_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [readperms] =&amp;gt; 1&lt;br /&gt;
                    [write] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [self_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [readperms] =&amp;gt; 1&lt;br /&gt;
                    [writeperms] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [group_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [read] =&amp;gt; 1&lt;br /&gt;
                    [readperms] =&amp;gt; 1&lt;br /&gt;
                    [write] =&amp;gt; 1&lt;br /&gt;
                    [delete] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    [facture] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [lire] =&amp;gt; 1&lt;br /&gt;
            [creer] =&amp;gt; 1&lt;br /&gt;
            [invoice_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [unvalidate] =&amp;gt; 1&lt;br /&gt;
                    [send] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [valider] =&amp;gt; 1&lt;br /&gt;
            [paiement] =&amp;gt; 1&lt;br /&gt;
            [supprimer] =&amp;gt; 1&lt;br /&gt;
            [facture] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [export] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    [propale] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [lire] =&amp;gt; 1&lt;br /&gt;
            [creer] =&amp;gt; 1&lt;br /&gt;
            [valider] =&amp;gt; 1&lt;br /&gt;
            [propal_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [send] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [cloturer] =&amp;gt; 1&lt;br /&gt;
            [supprimer] =&amp;gt; 1&lt;br /&gt;
            [export] =&amp;gt; 1&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    [produit] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [lire] =&amp;gt; 1&lt;br /&gt;
            [creer] =&amp;gt; 1&lt;br /&gt;
            [supprimer] =&amp;gt; 1&lt;br /&gt;
            [export] =&amp;gt; 1&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    etc... a lot of other rights...&lt;br /&gt;
&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When you are finished with implementing the support of CustomFields in the module, you should delete these debug lines.&lt;br /&gt;
&lt;br /&gt;
== Implementing the support of CustomFields in the module ==&lt;br /&gt;
&lt;br /&gt;
If there is a module you want to implement the support of CustomFields, but does not yet has the required components, here is a guide on how to implement these components.&lt;br /&gt;
&lt;br /&gt;
=== Hooks ===&lt;br /&gt;
&lt;br /&gt;
Implementing hooks will allow custom fields to appear on your module&#039;s page. They will also be editable and they will save the data. For creation, you need to use a trigger (see the subchapter below).&lt;br /&gt;
&lt;br /&gt;
First you should be familiar with the way that hooks are implemented in modules. If you don&#039;t, please first [[Hooks_system#Implementation|read the hooks documentation]].&lt;br /&gt;
&lt;br /&gt;
An example case of implementation of hooks into a Dolibarr core file can be found in the [[Module_CustomFields_Cases|CustomFields Example Cases wiki page]].&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Datasheet hook&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
To implement CustomFields in your module, you must initialize the hook manager, then use the following hook context:&lt;br /&gt;
* formObjectOptions&lt;br /&gt;
&lt;br /&gt;
This hook must be placed everywhere in your code where you want custom fields to appear (eg: on the creation page and datasheet view page).&lt;br /&gt;
&lt;br /&gt;
Note: Generally, you will have to place the hook just before the end of a table tag (&#039;&#039;&#039;&amp;lt;/table&amp;gt;&#039;&#039;&#039;), so if you don&#039;t know where to place it (eg: if you&#039;re implementing CustomFields on a Dolibarr core module), just search for this tag in the page shown in the url (eg: /htdocs/commande/fiche.php).&lt;br /&gt;
&lt;br /&gt;
Eg: First load the hookmanager at the beginning of your php script:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Initialize technical object to manage hooks of thirdparties. Note that conf-&amp;gt;hooks_modules contains array array&lt;br /&gt;
include_once(DOL_DOCUMENT_ROOT.&#039;/core/class/hookmanager.class.php&#039;);&lt;br /&gt;
$hookmanager=new HookManager($db);&lt;br /&gt;
$hookmanager-&amp;gt;initHooks(array(&#039;yourmodule&#039;));&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then place the hook as many times as required:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// ... a lot of php code ...&lt;br /&gt;
&lt;br /&gt;
print &#039;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&#039;; // end of the last row of a table&lt;br /&gt;
&lt;br /&gt;
// Other attributes&lt;br /&gt;
$parameters=array(&#039;colspan&#039; =&amp;gt; &#039; colspan=&amp;quot;3&amp;quot;&#039;); // specifying the colspan is optional but will help CustomFields know how to correctly print the table&lt;br /&gt;
$reshook=$hookmanager-&amp;gt;executeHooks(&#039;formObjectOptions&#039;,$parameters,$object,$action);    // Note that $action and $object may have been modified by hook&lt;br /&gt;
&lt;br /&gt;
print &#039;&amp;lt;/table&amp;gt;&#039;; // end of the table, this is what you&#039;re looking for, place the hook just above&lt;br /&gt;
&lt;br /&gt;
// ... a lot of php code ... redo the same if you find another &amp;lt;/table&amp;gt; and there&#039;s a page where there&#039;s no custom fields but you want to place custom fields on there&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note2: If after implementing the hook you receive an error or custom fields don&#039;t appear, you may have to change $object to another variable, depending on what is called the main object (eg: in Agenda it&#039;s called $action and $actioncomm).&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Lines hooks&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
For product lines, you should anyway use the product lines template files: htdocs/core/tpl/freeproductline_edit.tpl.php&lt;br /&gt;
&lt;br /&gt;
If you don&#039;t, you can still manually implement the required hooks, what is required is that you supply $line (which is the current line object being processed) in the $parameters array:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$parameters=array(&#039;line&#039;=&amp;gt;$line,&#039;fk_parent_line&#039;=&amp;gt;$line-&amp;gt;fk_parent_line);&lt;br /&gt;
&lt;br /&gt;
echo $hookmanager-&amp;gt;executeHooks(&#039;formEditProductOptions&#039;,$parameters,$this,$action);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hooks names:&lt;br /&gt;
* formEditProductOptions&lt;br /&gt;
* formCreateProductOptions ($line is not required here, because we create a new product line! you can leave $parameters=array(); empty).&lt;br /&gt;
Note: there&#039;s no hook for viewing, because showing the customfields will clutter the visual field, but if you really want you can also do it by adding a formViewProductOptions hook.&lt;br /&gt;
&lt;br /&gt;
=== Triggers ===&lt;br /&gt;
&lt;br /&gt;
First you need to know how to implement triggers in Dolibarr. For more informations about how to implement triggers in your module, please read the [[Triggers#Manage_and_create_a_new_trigger.27s_action|triggers wiki page]].&lt;br /&gt;
&lt;br /&gt;
An example case of implementation of hooks into a Dolibarr core file can be found in the [[Module_CustomFields_Cases|CustomFields Example Cases wiki page]].&lt;br /&gt;
&lt;br /&gt;
Implementing triggers on your module will allow CustomFields to save the fields at creation (datasheet edition is already managed by hooks).&lt;br /&gt;
&lt;br /&gt;
You need to implement 3 triggers:&lt;br /&gt;
- on creation of object - eg: YOURMODULE_CREATE&lt;br /&gt;
- on creation of lines - eg: YOURMODULELINE_INSERT (optional, only if your module has product lines)&lt;br /&gt;
- on update of lines - eg: YOURMODULELINE_UPDATE (optional, only if your module has product lines)&lt;br /&gt;
&lt;br /&gt;
Note: you need to supply the sql id of the newly created object before calling the trigger.&lt;br /&gt;
&lt;br /&gt;
Eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$this-&amp;gt;id = $this-&amp;gt;db-&amp;gt;last_insert_id(MAIN_DB_PREFIX.&amp;quot;yourmodule_table_element&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
// Appel des triggers&lt;br /&gt;
include_once(DOL_DOCUMENT_ROOT . &amp;quot;/core/class/interfaces.class.php&amp;quot;);&lt;br /&gt;
$interface=new Interfaces($this-&amp;gt;db);&lt;br /&gt;
$result=$interface-&amp;gt;run_triggers(&#039;YOURMODULE_CREATE&#039;,$this,$user,$langs,$conf);&lt;br /&gt;
if ($result &amp;lt; 0) {&lt;br /&gt;
    $error++; $this-&amp;gt;errors=$interface-&amp;gt;errors;&lt;br /&gt;
}&lt;br /&gt;
// Fin appel triggers&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note2: if it&#039;s a Dolibarr core module, you can easily find the right place to put the trigger by searching for &#039;create sql&#039; in the class subfolder (eg: URL is /htdocs/commande/fiche.php, the class is in /htdocs/commande/class/commande.class.php).&lt;br /&gt;
&lt;br /&gt;
Note3: CustomFields also supports a few other triggers like _CLONE, _UPDATE, _MODIFY, but they are not required and may not work depending on how your module works to manage these actions.&lt;br /&gt;
&lt;br /&gt;
== Configuring CustomFields to support the module ==&lt;br /&gt;
&lt;br /&gt;
=== Preliminary work ===&lt;br /&gt;
&lt;br /&gt;
You need to get the parameters of this module, and you will then put them in the CustomFields configuration file (conf_customfields.lib.php).&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Getting the parameters automatically&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
You can use the [[Module_CustomFields_Dev#Preliminary_test|preliminary test]] to easily get all those parameters.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Getting the parameters manually&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
To configure CustomFields to support a module, you need at least 5 parameters:&lt;br /&gt;
* the module&#039;s table_name (name of the sql table where the module stores its datas)&lt;br /&gt;
* the module&#039;s context (name given by the module to the hookmanager)&lt;br /&gt;
* the module&#039;s idvar (name of the variable that holds the sql row id of the record)&lt;br /&gt;
* the module&#039;s trigger action(s) (triggers on creation and update, see the related chapter above)&lt;br /&gt;
* the module&#039;s rights (for security purposes)&lt;br /&gt;
&lt;br /&gt;
To get these parameters manually, you can take a look inside the code of the module&#039;s files you want to implement:&lt;br /&gt;
* module&#039;s table name: either find it by yourself in the database (looks like llx_themodulename) or by printing it in actions_customfields.class.php by adding &#039;&#039;&#039;print_r($parameters)&#039;&#039;&#039; and search for $parameters-&amp;gt;table_element&lt;br /&gt;
* module&#039;s context: search for &amp;quot;callHooks(&amp;quot; without the quotes or take a look at the wiki: http://wiki.dolibarr.org/index.php/Hooks_system&lt;br /&gt;
or you can get it by printing it in the /htdocs/customfields/class/actions_customfields.class.php by adding print_r($parameters) and search for $parameters-&amp;gt;context&lt;br /&gt;
* module&#039;s trigger actions: search for &amp;quot;run_triggers(&amp;quot; or take a look at the wiki: http://wiki.dolibarr.org/index.php/Triggers#List_of_known_triggers_actions&lt;br /&gt;
* module&#039;s idvar: look at the url when you access one of the module&#039;s record, and you should see something like fiche.php?xxxid=12 - you need the xxxid (eg: socid, facid, etc.).&lt;br /&gt;
* module&#039;s security: in the page shown in URL when accessing the module&#039;s datasheet, edit this file and add &#039;&#039;&#039;print($user-&amp;gt;rights)&#039;&#039;&#039;, then in the list try to find the correct right (generally something like $user-&amp;gt;modulename-&amp;gt;creer). You can also try to search for $user-&amp;gt;rights in the sourcecode of this module.&lt;br /&gt;
&lt;br /&gt;
With these values, edit the config file (/htdocs/customfields/conf/conf_customfields.lib.php), particularly the $modulesarray and $triggersarray variables:&lt;br /&gt;
&lt;br /&gt;
=== Configure hooks and main parameters ===&lt;br /&gt;
&lt;br /&gt;
Add every parameters except triggers in the $modulesarray:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$modulesarray = array(&lt;br /&gt;
                      array(&#039;context&#039;=&amp;gt;&#039;invoicecard&#039;, &#039;table_element&#039;=&amp;gt;&#039;facture&#039;, &#039;idvar&#039;=&amp;gt;&#039;facid&#039;, &#039;rights&#039;=&amp;gt;array(&#039;$user-&amp;gt;rights-&amp;gt;facture-&amp;gt;creer&#039;)), // Client Invoice&lt;br /&gt;
                      array(&#039;context&#039;=&amp;gt;&#039;membercard&#039;, &#039;table_element&#039;=&amp;gt;&#039;adherent&#039;, &#039;idvar&#039;=&amp;gt;&#039;rowid&#039;, &#039;rights&#039;=&amp;gt;array(&#039;$user-&amp;gt;rights-&amp;gt;adherent-&amp;gt;creer&#039;), &#039;tabs&#039;=&amp;gt;array(&#039;objecttype&#039;=&amp;gt;&#039;member_admin&#039;, &#039;function&#039;=&amp;gt;&#039;member_admin_prepare_head&#039;, &#039;lib&#039;=&amp;gt;DOL_DOCUMENT_ROOT.&#039;/core/lib/member.lib.php&#039;)), // Members / Adherents&lt;br /&gt;
                     ); // Edit me to add the support of another module - NOTE: Lowercase only!&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are also a lot of others parameters you can give to CustomFields so that the module gets better managed or to add a few other functionnalities, here is a non-exhaustive list:&lt;br /&gt;
* context: name of the module&#039;s context.&lt;br /&gt;
* table_element: name of the sql table that holds the object&#039;s datas.&lt;br /&gt;
* idvar: name of the variable that holds the id of the object in the sql database (by default &#039;id&#039; or &#039;rowid&#039;).&lt;br /&gt;
* rights: prevent the custom fields to be editable unless all the rights set here are enabled (preventing custom fields to be viewed is useless because they won&#039;t show anyway if the page can&#039;t be loaded due to insufficient rights, so this is directly handled by the module).&lt;br /&gt;
* tabs=&amp;gt;array(objecttype, function, lib, tabname, tabtitle): used to embed CustomFields&#039;s admin panel into another module&#039;s admin page.&lt;br /&gt;
&lt;br /&gt;
=== Configure creation triggers ===&lt;br /&gt;
&lt;br /&gt;
Add the triggers in $triggersarray:&lt;br /&gt;
$triggersarray = array(&amp;quot;order_create&amp;quot;=&amp;gt;&amp;quot;commande&amp;quot;,&lt;br /&gt;
                       &amp;quot;yourmodule_triggeraction1&amp;quot;=&amp;gt;&amp;quot;yourmodule_tablename&amp;quot;,&lt;br /&gt;
                       &amp;quot;yourmodule_triggeraction2&amp;quot;=&amp;gt;&amp;quot;yourmodule_tablename&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
Note: generally you will be looking to implement the _CREATE.&lt;br /&gt;
&lt;br /&gt;
Note2: if a triggeraction is not recognized by CustomFields (eg: mymodule_addpayment) then by default CustomFields will assimilate it to a _create action (it will create and save the values for the custom fields, just for a module&#039;s creation page).&lt;br /&gt;
&lt;br /&gt;
Note3: CustomFields also supports a few other triggers like _CLONE, _UPDATE, _MODIFY, but they are not required and may not work depending on how your module works to manage these actions.&lt;br /&gt;
&lt;br /&gt;
=== Configuration troubleshooting ===&lt;br /&gt;
&lt;br /&gt;
* If you cannot see the custom fields appear on your module&#039;s page, this means the hooks are not implemented or configured in CustomFields, please read the related chapters.&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet but there&#039;s no edit button icon: the module implements security but you did not configure it properly in CustomFields, please find the right security in $user-&amp;gt;rights and set it in conf_customfields.lib.php.&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the creation page but are not saved: you forgot to set the trigger in $triggersarray, or the creation trigger does not exist for this module and you have to implement it. Jump to [[Module_CustomFields_Dev#Triggers]].&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet but not on the creation page (or vice versa): A hook is missing on the page where the custom fields do not appear. Jump to [[Module_CustomFields_Dev#Hooks]].&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet with the edit button, but when the edit button is clicked, everything disappear: either the $idvar is not properly set (look at the URL to know if you need to set idvar), or the hook is not properly made ($object is not defined and it&#039;s called by another name, but the hook use $object instead of the correct variable).&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet with the edit button, can be edited but cannot be saved (the data is not saved): same as the previous point: either $idvar or $object bug.&lt;br /&gt;
&lt;br /&gt;
Also you might find useful to &#039;&#039;&#039;[[Module_CustomFields#General_advices_for_troubleshooting|enable php errors]]&#039;&#039;&#039; to better track errors if that&#039;s not already done.&lt;br /&gt;
&lt;br /&gt;
= Example cases =&lt;br /&gt;
&lt;br /&gt;
You can find a compilation of concrete, practical examples on the [[Module_CustomFields_Cases]] wiki page.&lt;br /&gt;
&lt;br /&gt;
= TO DO =&lt;br /&gt;
&lt;br /&gt;
You will find below the public list of features that should be implemented in the future.&lt;br /&gt;
&lt;br /&gt;
If you have an idea about a new feature that isn&#039;t listed below, feel free to add it to the list!&lt;br /&gt;
&lt;br /&gt;
== To Do ==&lt;br /&gt;
&lt;br /&gt;
* Better management of hidden fields with custom cascading (eg: works great on creation form, but on edit form and creation form with edit action, if the parents already have a value, the hidden fields will stay hidden! This is because of the AJAX is not called, and this is normal). -&amp;gt; Propose a better custom cascade function storing the hidden field&#039;s state inside extraoptions?&lt;br /&gt;
&lt;br /&gt;
== To Document ==&lt;br /&gt;
* Automatic recopy on cloning for custom fields and products&#039; lines custom fields.&lt;br /&gt;
* new API function: customfields_clone_or_recopy($object, $fromobject, $action2 = null)&lt;br /&gt;
&lt;br /&gt;
== Should do ==&lt;br /&gt;
* Add the possibility to mark a custom field to be shown in tables listings (eg: as a column in products listings) with &amp;quot;order by this column&amp;quot; buttons and stuffs. Will be difficult if the hooks are not yet implemented in listings.&lt;br /&gt;
* Checkbox (multiple checkboxes, one for each possible value) and multiselect (big select and can use SHIFT to multiselect) (store in XML file) (but impossible with constrained fields, can&#039;t store multiple foreign keys ids in one single sql field, but would work for other types like enum)&lt;br /&gt;
use fieldset, input checkbox and label for, eg:&lt;br /&gt;
    &amp;lt;fieldset&amp;gt;&amp;lt;legend&amp;gt;&amp;lt;label for=&amp;quot;cf_productslist&amp;quot;&amp;gt; $langs-&amp;gt;trans(&#039;cf_productslist&#039;)&amp;lt;/label&amp;gt;&amp;lt;/legend&amp;gt;&lt;br /&gt;
    &amp;lt;input type=&amp;quot;checkbox&amp;quot; name=&amp;quot;cf_productslist_choice1&amp;quot; value=&amp;quot;1&amp;quot; id=&amp;quot;cf_productslist_choice1&amp;quot; /&amp;gt;&amp;lt;label for=&amp;quot;cf_productslist_choice1&amp;quot;&amp;gt; $langs-&amp;gt;trans(&#039;cf_productslist_choice1&#039;)&amp;lt;/label&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
    &amp;lt;input type=&amp;quot;checkbox&amp;quot; name=&amp;quot;cf_productslist_choice2&amp;quot; value=&amp;quot;1&amp;quot; id=&amp;quot;cf_productslist_choice2&amp;quot; /&amp;gt;&amp;lt;label for=&amp;quot;cf_productslist_choice2&amp;quot;&amp;gt; $langs-&amp;gt;trans(&#039;cf_productslist_choice2&#039;)&amp;lt;/label&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
    etc...&lt;br /&gt;
    &amp;lt;/fieldset&amp;gt;&lt;br /&gt;
* Add a javascript options values generator for the enum type (a hidden input that would be shown only when DropdownBox type is selected and that would permit to add options by clicking a plus button).&lt;br /&gt;
* Replace triggers in customfields class by hookmanager?&lt;br /&gt;
* AJAX previsualisation of the HTML custom field that will be created.&lt;br /&gt;
* In ODTs, create special tags to directly access day, month or year value of a date type field independently of the rest.&lt;br /&gt;
* New overloading function&#039;s action: &amp;quot;get&amp;quot;, similar to view except that it works not only on Dolibarr&#039;s view but whenever someone tries to access this field&#039;s value.&lt;br /&gt;
* Add Upload and Image field types (this would allow for an easier integration inside ODT and PDF templates than the current workflow of Dolibarr).&lt;br /&gt;
&lt;br /&gt;
== Known bugs ==&lt;br /&gt;
* in product and service modules, if you edit a field, the proposals and other fields below won&#039;t be shown, you need to refresh the page. This problem resides in Dolibarr I think (since we are simply using a hook).&lt;br /&gt;
* ficheinter (Intervention module) custom fields will stay in create mode just after create page is submitted (this is because of the $action that is not standard - $action=edit or something).&lt;br /&gt;
&lt;br /&gt;
== Never/Maybe one day ==&lt;br /&gt;
* Add support for repeatable (predefined) invoices (the way it is currently managed makes it very difficult to manage this without making a big exception, adding specific functions in customfields modules that would not at all will be reusable anywhere else, when customfields has been designed to be as generic as possible to support any module and any version of dolibarr, because it&#039;s managed by a totally different table while it&#039;s still managed by the same module, CustomFields work with the paradigm: one module, one table).&lt;br /&gt;
* Add an AJAX select box for constrained values to automatically select the appropriate name for Smart Value Substitution : when a constrained type is selected and a table is selected, a hidden select box would show up with the list of the fields of this table to choose the values that will be printed as the values for this customfield (eg: for table llx_users you could select the &amp;quot;nom&amp;quot; field and then it would automatically prepend &amp;quot;nom_&amp;quot; to the field&#039;s name).&lt;br /&gt;
* Fine-grained rights management: rights per group, per user, and per field.&lt;br /&gt;
* Replace overloading functions (extend class) by hookmanager if possible (but how to simulate the *full switch? Plus this will force users to make their own modules to make those functions, and to disable/renable their modules everytime they will add a new function...).&lt;br /&gt;
* Refactor the trigger array: merge it with the modulesarray (along with a new way to specify which customfields trigger action a trigger should correspond, eg: &#039;linebill_insert&#039;=&amp;gt;&#039;customfields_create&#039;). But the triggers aren&#039;t necessarily associated with a specific module...&lt;br /&gt;
* AJAX autocompletion / auto population of field&#039;s content when typing the beginning of a value in a text box.&lt;/div&gt;</summary>
		<author><name>Lrq3000</name></author>
	</entry>
	<entry>
		<id>https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Dev&amp;diff=33179</id>
		<title>Module CustomFields Dev</title>
		<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Dev&amp;diff=33179"/>
		<updated>2015-02-24T14:15:15Z</updated>

		<summary type="html">&lt;p&gt;Lrq3000: /* Never/Maybe one day  */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:CustomFields]]&lt;br /&gt;
{{TemplateDocDevEn}}&lt;br /&gt;
{{TemplateModEN}}&lt;br /&gt;
{{BasculeDevUserEn|&lt;br /&gt;
name=CustomFields|&lt;br /&gt;
num=8500|&lt;br /&gt;
devdoc=This page|&lt;br /&gt;
userdoc=[[Module_CustomFields]]|}}&lt;br /&gt;
[[Category:FAQ EN]]&lt;br /&gt;
&lt;br /&gt;
These notes are aimed to developpers who want to extend the functionnalities of the module or want to use it for extreme cases.&lt;br /&gt;
This is not meant to guide implementers on how to implement and configure the module (see the related chapter for that).&lt;br /&gt;
&lt;br /&gt;
If you are an implementer and just want to use CustomFields with its normal functions, you can read the [[Module_CustomFields|user documentation of CustomFields]].&lt;br /&gt;
&lt;br /&gt;
You can also jump directly to [[Module_CustomFields_Cases]] wiki page for a collection of practical examples.&lt;br /&gt;
&lt;br /&gt;
= Principles =&lt;br /&gt;
&lt;br /&gt;
CustomFields is simply a wrapper for SQL databases.&lt;br /&gt;
&lt;br /&gt;
The module is based on SQL to manage the fields and most of the stuff. It uses and understands standard SQL.&lt;br /&gt;
&lt;br /&gt;
The main principles of CustomFields are:&lt;br /&gt;
&lt;br /&gt;
* one SQL column = one Dolibarr field.&lt;br /&gt;
* one SQL type = one method to show and edit the field in Dolibarr.&lt;br /&gt;
* almost everything should be configurable from the configuration file, or admin interface.&lt;br /&gt;
* be independent from Dolibarr&#039;s data structures.&lt;br /&gt;
&lt;br /&gt;
CustomFields sits besides Dolibarr, and does not mix with it, even if in the Dolibarr&#039;s interface it looks like CustomFields is embedded inside Dolibarr, in reality, CustomFields is totally independent.&lt;br /&gt;
&lt;br /&gt;
= Goals =&lt;br /&gt;
&lt;br /&gt;
This module was made to provide an easy way to manage custom sql fields, not only for users and implementers, but also for developpers to use in their own module:&lt;br /&gt;
* by easily adding the support of custom fields for your own module by using this module;&lt;br /&gt;
* by reusing custom fields from another module in your own module;&lt;br /&gt;
* by managing sql fields in your own module by using the CustomFields class (which will take care of everything from printing, editing to PDF/ODT templating).&lt;br /&gt;
&lt;br /&gt;
So this module is not only an auxiliary of other modules, but it can also be the core part of your own module by using it as your SQL query class for fields.&lt;br /&gt;
&lt;br /&gt;
= SQL structure =&lt;br /&gt;
&lt;br /&gt;
A CustomFields table needs to respect three parameters that never change:&lt;br /&gt;
&lt;br /&gt;
* table&#039;s name (format: llx_*dolibarrmodule*_customfields where *dolibarrmodule* = table_element in conf_customfields.lib.php)&lt;br /&gt;
* first column: rowid, which is the primary key for each field&lt;br /&gt;
* second column: fk_*dolibarrmodule* which is the foreign key that links every customfields record to the Dolibarr&#039;s module that is associated (invoice, order, etc...).&lt;br /&gt;
&lt;br /&gt;
Apart from the table&#039;s name and the first two special columns, every other columns are just standard sql columns that you can freely create, edit or delete as you want, from the module&#039;s administration page or from a third-party SQL management tool (such as phpMyAdmin).&lt;br /&gt;
&lt;br /&gt;
You can also create more complex sql fields: constraints, foreign keys, transformations, other sql types, etc...).&lt;br /&gt;
&lt;br /&gt;
Then the module will try to manage the sql fields you manually created the best it can:&lt;br /&gt;
* by printing appropriate input fields in the Dolibarr gui interface.&lt;br /&gt;
* by providing the developper an easy access to these fields in php (eg: $customfields-&amp;gt;cf_myfield or $object-&amp;gt;customfields-&amp;gt;cf_myfield) for any use, including PDF templates and ODT templates, or in any php script (so that you can use these fields in your own module).&lt;br /&gt;
&lt;br /&gt;
= SQL Indexes =&lt;br /&gt;
&lt;br /&gt;
The module automatically adds an index on the primary key and foreign key (first and second columns).&lt;br /&gt;
&lt;br /&gt;
To further optimize the performances of your database, and particularly the customfields tables, you may want to manually add indexes based on usage.&lt;br /&gt;
&lt;br /&gt;
Also, you may add indexes on remote tables used by constrained fields, which could optimize even more the performances since constrained fields have the worst performance drawback for CustomFields.&lt;br /&gt;
&lt;br /&gt;
= SQL Compatibility mode =&lt;br /&gt;
&lt;br /&gt;
The module relies on referential integrity checks and foreign keys of your DBMS to keep the database clean and to provide the constrained fields feature. Referential integrity checks is part of the SQL standards and should be used whenever possible, which always was a main goal for CustomFields (to be respectful of SQL standards).&lt;br /&gt;
&lt;br /&gt;
Unluckily, a few DBMS systems still don&#039;t support this critical functionality of the SQL standard, for example MySQL enables MyISAM engine by default, which doesn&#039;t support this feature.&lt;br /&gt;
&lt;br /&gt;
In these cases, you can either do two things:&lt;br /&gt;
* Switch to an engine which support referential integrity. For example with MySQL, you can enable InnoDB, which support referential integrity checks and foreign keys.&lt;br /&gt;
* Use CustomFields compatibility mode (more infos below).&lt;br /&gt;
&lt;br /&gt;
In the case your database does not support referential integrity checks, CustomFields will automatically switch to the SQL compatibility mode, which will allow you to use CustomFields even on databases where foreign keys are not possible.&lt;br /&gt;
&lt;br /&gt;
In compatibility mode, you will get nearly all CustomFields advanced features such as Constrained fields, but the customfields&#039; tables in the database won&#039;t be cleaned automatically (no delete on cascade, meaning that when you delete an object, the linked custom fields will still be kept in your database), and constrained fields won&#039;t be updated automatically on update.&lt;br /&gt;
&lt;br /&gt;
Nevertheless, these missing cleanup functionalities won&#039;t be seen by your users because they will never be accessible: on deletion of an object, the custom fields won&#039;t ever be accessible again from Dolibarr&#039;s interface, and object&#039;s ids are never changed since Dolibarr also relies on this. Thus in compatibility mode, CustomFields will act just like normally, but your database will be a bit less clean.&lt;br /&gt;
&lt;br /&gt;
= JSON extension for extra options =&lt;br /&gt;
&lt;br /&gt;
== Introduction to JSON in CustomFields ==&lt;br /&gt;
The relational model is very great to store and fetch data with a declarative language, and this is very powerful, but this model also imposes a few limitations, for example the relations must be well formatted and predefined.&lt;br /&gt;
&lt;br /&gt;
That&#039;s why we finally implemented a &#039;&#039;&#039;JSON extension&#039;&#039;&#039; to manage &#039;&#039;&#039;extra options&#039;&#039;&#039; and associate them with custom fields. This allows to add many properties (or columns if you are more used to the relational model) on-the-fly without having to predefine them prior to storing and using them.&lt;br /&gt;
&lt;br /&gt;
This allows to add an undefined number of columns/properties/extra options that will help manage special behaviours for custom fields. For example, it is currently used to allow to change the order of appearance of custom fields, and to store and manage remote table and columns datas for constrained fields on DBMS that do not support Foreign Keys checks.&lt;br /&gt;
&lt;br /&gt;
We chose JSON over XML because the library are simpler and the data footprint smaller, and because CustomFields does not need advanced features of XML like XQuery, data typing and meta structures (at least not for now).&lt;br /&gt;
&lt;br /&gt;
The other advantage is that php objects can directly be encoded into JSON without any preprocessing, and can then as straightly be decoded from JSON back into a php object.&lt;br /&gt;
&lt;br /&gt;
A simple mean to encode and decode back an object into and from a storage place, as well as allow users to easily add/remove extra options were the main requirement, and JSON (+ JSON libraries for PHP) meet this criteria.&lt;br /&gt;
&lt;br /&gt;
However, note that the extra options are now encoded as an associative array, so as to avoid any ambiguities (there&#039;s no associative array in JSON, they are converted to objects).&lt;br /&gt;
&lt;br /&gt;
== Implementation of JSON ==&lt;br /&gt;
The implementation is very simple: a table called &#039;&#039;&#039;llx_customfields_extraoptions&#039;&#039;&#039; is created when you enable the CustomFields module, and this table only contains three columns:&lt;br /&gt;
* table_name (varchar)&lt;br /&gt;
* column_name (varchar)&lt;br /&gt;
* extraoptions (blob)&lt;br /&gt;
&lt;br /&gt;
(table_name, column_name) both forms the primary key of the table, and links to columns (custom fields) of other customfields tables.&lt;br /&gt;
extraoptions is a blob, which allows to store the data as binary (as-is) without encoding (which solves a big problem of JSON which is encoding special and foreign characters, here there is no problem at all).&lt;br /&gt;
&lt;br /&gt;
When fetching custom fields, the module simply JOIN the extraoptions table, which adds an &#039;&#039;&#039;extraoptions&#039;&#039;&#039; column for each custom field in the SQL result. Then it is quite easy to just json_decode() the data returned inside the extraoptions column.&lt;br /&gt;
&lt;br /&gt;
This methodology allows to have all the advantages of a relational database, with the flexibility of a free-schema model:&lt;br /&gt;
* everything is stored inside the database (only one storage place)&lt;br /&gt;
* fixed columns allows for relational joining and optimization&lt;br /&gt;
* thus queries (fetching) are optimised&lt;br /&gt;
* at the same time, the extraoptions are automatically retrieved and associated with every custom field, and whenever needed they can be used&lt;br /&gt;
* and thus we also get a flexible model to &#039;&#039;&#039;add extra (virtual) columns whenever needed&#039;&#039;&#039; without having to alter the relational database schema&lt;br /&gt;
&lt;br /&gt;
Thus no critical information is and should be stored inside the extraoptions column because they would not be query-able, only extra options that are just used to tweak the management of those fields inside the PHP application should be stored. But this is a wonderful way to add non critical informations that still allows to develop more complex features and management for special custom fields.&lt;br /&gt;
&lt;br /&gt;
== CustomFields JSON API ==&lt;br /&gt;
&lt;br /&gt;
=== Storing extra options ===&lt;br /&gt;
&lt;br /&gt;
You can use the &#039;&#039;&#039;setExtra()&#039;&#039;&#039; method to set extra options of a custom field.&lt;br /&gt;
&lt;br /&gt;
Eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$extra = array(); // $extra should always be an associative array, it&#039;s no longer an object&lt;br /&gt;
$extra[&#039;myproperty&#039;] = &#039;Anything I want here&#039;; // set a property/extra option like this&lt;br /&gt;
$extra[&#039;category&#039;][&#039;subcategory&#039;][&#039;prop&#039;] = true; // we can set recursive properties, there&#039;s not any limit to how deep the hierarchy can be&lt;br /&gt;
&lt;br /&gt;
require_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomField($db, &#039;currentmodule&#039;);&lt;br /&gt;
$fieldid = &#039;mycustomfields&#039;; // $fieldid can either be a column_name or an ordinal_position from information_schema.columns&lt;br /&gt;
$result = $customfields-&amp;gt;setExtra($fieldid, $extra); // store the extra options inside the database&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can also use addCustomField() or updateCustomField() with an $extra array, and these functions will also call the setExtra() method.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: the extra options you set are &#039;&#039;&#039;appended&#039;&#039;&#039; to the ones already stored in the database. However, you can also overwrite previous options, just use the same name of property but with a different value (the replacement is recursive, you can do it at any level in the hierarchy).&lt;br /&gt;
&lt;br /&gt;
=== Fetching extra options ===&lt;br /&gt;
&lt;br /&gt;
Extra options are automatically fetched along with a custom field structure, decoded, and stored inside the extra sub-property of any field object, or CustomFields object.&lt;br /&gt;
&lt;br /&gt;
When fetched, extra options are stored as a sub-object for the current field object (which itself is stored as a subobject of the CustomFields object if caching is enabled).&lt;br /&gt;
&lt;br /&gt;
Eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
require_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomField($db, &#039;currentmodule&#039;);&lt;br /&gt;
$fields = fetchAllFieldsStruct(); // fetching fields structure will automatically fetch extra options at the same time. This works with fetchAllFieldsStruct() and fetchFieldStruct()&lt;br /&gt;
&lt;br /&gt;
// Now that you fetched the fields structure, you can just access the extra options sub-properties:&lt;br /&gt;
print_r($fields-&amp;gt;mycustomfield-&amp;gt;extra); // will print all extra options for the field mycustomfield&lt;br /&gt;
print_r($customfields-&amp;gt;fields-&amp;gt;mycustomfield-&amp;gt;extra); // you can also access the same field(s) directly from the CustomFields object if caching is enabled (default behaviour)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Modifying extra options ===&lt;br /&gt;
&lt;br /&gt;
You can just combine the two previous points to edit extra options of a custom field: just load a custom field, edit on-the-fly the extra property with PHP code, and then store it back directly into the database:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
require_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomField($db, &#039;currentmodule&#039;);&lt;br /&gt;
$fields = fetchAllFieldsStruct();&lt;br /&gt;
&lt;br /&gt;
$fields-&amp;gt;mycustomfield-&amp;gt;extra[&#039;newproperty&#039;] = &#039;This is a new property&#039;; // add a new property over the old extra options sub-object&lt;br /&gt;
$fields-&amp;gt;mycustomfield-&amp;gt;extra[&#039;mycategory&#039;] = &#039;Overwrite the old category&#039;; // this will replace the previous value of &#039;mycategory&#039; extra option&lt;br /&gt;
&lt;br /&gt;
$customfields-&amp;gt;setExtra(&#039;mycustomfield&#039;, $fields-&amp;gt;mycustomfield-&amp;gt;extra); // store the modified extra options back into the database for future usage&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= CustomFields API =&lt;br /&gt;
&lt;br /&gt;
== Introduction to CustomFields API ==&lt;br /&gt;
&lt;br /&gt;
This chapter will cover the basics on how to manually and concretely use the CustomFields class to create/update/fetch/manage custom fields.&lt;br /&gt;
&lt;br /&gt;
This will give you a better understanding of how CustomFields works, as well as giving you insights on how to use it for your own applications.&lt;br /&gt;
&lt;br /&gt;
Important note: contrary to the Facade API, the core CustomFields class use columns names without any prefix, so don&#039;t try to use the prefix, it won&#039;t work! For example: if your field is named &#039;mycustomfield&#039; and prefix is &#039;cf_&#039;, when you use the Facade API you should use &#039;$object-&amp;gt;$path-&amp;gt;cf_mycustomfield&#039; but when you use the core CustomFields class directly, $fieldname = &#039;mycustomfield&#039;.&lt;br /&gt;
&lt;br /&gt;
== Prerequisite to use the CustomFields class ==&lt;br /&gt;
&lt;br /&gt;
Before using the CustomFields class, you first must instanciate it. This is a necessary preliminary step before using any of the methods that will be covered inside this chapter, and thus will not be repeated, only shown once here.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Include the CustomFields class (which contains the main API)&lt;br /&gt;
require_once(dirname(__FILE__).&#039;/../class/customfields.class.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Instanciate a CustomFields object&lt;br /&gt;
global $db; // the database handler created by Dolibarr&lt;br /&gt;
$table_element = &#039;currentmodule&#039;; // where $table_element is the database&#039;s table name for the module, without the Dolibarr prefix (eg: for &#039;llx_facture&#039;, table_element is &#039;facture&#039;). You can also find it inside the $object properties (just print_r() it).&lt;br /&gt;
$customfields = new CustomFields($db, $table_element); // instanciate the CustomFields class inside $customfields object&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
CustomFields keeps track of errors that can happen when executing methods and SQL queries inside the &#039;&#039;&#039;error&#039;&#039;&#039; and &#039;&#039;&#039;errors&#039;&#039;&#039; properties, which you can print with the printErrors() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Do something with CustomFields before&lt;br /&gt;
&lt;br /&gt;
if (count($customfields-&amp;gt;errors) &amp;gt; 0) { // If there is at least one error&lt;br /&gt;
    $customfields-&amp;gt;printErrors(); // print errors&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// You can also print your own error message, in two manners:&lt;br /&gt;
&lt;br /&gt;
// 1- submit your own message&lt;br /&gt;
$customfields-&amp;gt;printErrors(&#039;Your own error message here&#039;); // this will print ONLY your error message, using the Dolibarr&#039;s AJAX error dialog (standard way of printing errors)&lt;br /&gt;
&lt;br /&gt;
// 2- append your message in the customfields errors&lt;br /&gt;
$customfields-&amp;gt;errors[] = &#039;Your own error message to be appended with other CustomFields messages&#039;;&lt;br /&gt;
$customfields-&amp;gt;printErrors(); // Here your error message will be appended, thus all error messages will be printed. This allows you for example to add more intelligible informations on a specific error.&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== General purpose SQL methods ==&lt;br /&gt;
&lt;br /&gt;
CustomFields provide an interface to easily execute various common SQL requests, as well as your own custom SQL requests.&lt;br /&gt;
&lt;br /&gt;
* fetchAllTables() fetch a list of all the tables in the database&lt;br /&gt;
* fetchPrimaryField($table) fetch the column_name (and only the column_name!) of the primary field of a table. You can then use fetchFieldStruct()  with the column_name to get any information you want.&lt;br /&gt;
* fetchAny($columns, $table, $where=&#039;&#039;, $orderby=&#039;&#039;, $limitby=&#039;&#039;) allows you to issue any simple SQL query command, for example fetchAny(&#039;*&#039;, &#039;sometable&#039;) is equivalent to SELECT * FROM sometable; (but then everything is managed automatically and result is returned to you)&lt;br /&gt;
* executeSQL($sql, $eventname) to execute any SQL query you want, this will return you a resource (and NOT a processed nuplet like fetchAny!). $eventname can be anything, it&#039;s just for Dolibarr logging facility.&lt;br /&gt;
* executeMultiSQL($sql, $eventname) to execute multiple SQL queries&lt;br /&gt;
* fetchFieldStruct($id=null, $nohide=false, $table=null, $cachepath=null) allows you to fetch the field&#039;s SQL structure of not only custom fields, but of any database&#039;s column using the $table variable. Eg: fetchFieldStruct(&#039;mycolumn&#039;, true, &#039;mytable&#039;);&lt;br /&gt;
* probeTable($table) helps you to check if a table exists in the database&lt;br /&gt;
* fetch_object($resource) will return a PHP object from a $resource, similarly to mysql_fetch_object() but with two advantages: it works on several DBMS (not only mysql), and it always return lowercase properties names (else depending on the configuration of the DBMS, the request can return uppercase or mixed case properties names).&lt;br /&gt;
* escape() and reverse_escape() are similare to mysql_real_escape but is reversible and DBMS-independent (so you can use it with other DBMSes).&lt;br /&gt;
&lt;br /&gt;
== Managing CustomFields structure ==&lt;br /&gt;
&lt;br /&gt;
The first step into CustomFields is to create and manage the structure of a custom field.&lt;br /&gt;
&lt;br /&gt;
=== Creating a custom field definition ===&lt;br /&gt;
&lt;br /&gt;
To create a custom field, you can use the addCustomField() method.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;addCustomField($fieldname, $type, $size, $nulloption, $defaultvalue = null, $constraint = null, $customtype = null, $customdef = null, $customsql = null, $fieldid = null, $extra = null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will take care of forging the (complicated) SQL query with the forgeSQLCustomField() method, and will return you an error if there was a problem.&lt;br /&gt;
&lt;br /&gt;
=== Updating a custom field definition ===&lt;br /&gt;
&lt;br /&gt;
To update a custom field, you can use the updateCustomField() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;&lt;br /&gt;
updateCustomField($fieldid, $fieldname, $type, $size, $nulloption, $defaultvalue, $constraint = null, $customtype = null, $customdef = null, $customsql = null, $extra = null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: the updateCustomField() and addCustomField() methods can both be used since updateCustomField() is in fact an alias for addCustomField(). CustomFields will automatically detect if a custom field already exists, and update it if that&#039;s true, or create it.&lt;br /&gt;
&lt;br /&gt;
Note2: updating a custom field will automatically take care of the constraints and foreign keys on its own, thus you can change the foreign key, or remove it altogether if set to null.&lt;br /&gt;
&lt;br /&gt;
=== Delete a custom field definition ===&lt;br /&gt;
&lt;br /&gt;
You can easily delete a custom field by column_name or by ordinal_position:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;deleteCustomField($id);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: constraints and foreign keys will also be deleted automatically so you shouldn&#039;t encounter any error.&lt;br /&gt;
&lt;br /&gt;
=== Fetch one custom field&#039;s SQL structure ===&lt;br /&gt;
&lt;br /&gt;
To get the SQL structure of a custom field, which can be quite useful to know for example its datatype or if it has a foreign key, or accepts null values, you can use the fetchFieldStruct() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
fields = $customfields-&amp;gt;fetchFieldStruct($id=null, $nohide=false, $table=null, $cachepath=null); // where $id can be a column_name, an ordinal_position from information_schema.columns or null if you want to fetch all the columns at once&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method will either return null, either a single field object, either an array on field objects.&lt;br /&gt;
&lt;br /&gt;
Note: this function will use caching by default to accelerate future results, but you can disable it, or even change the default cache path. For more informations please read the comments in the source code.&lt;br /&gt;
&lt;br /&gt;
=== Fetch several custom fields&#039; SQL structures ===&lt;br /&gt;
&lt;br /&gt;
Use the fetchAllFieldsStruct() method, which is an alias for fetchFieldStruct():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;fetchAllFieldsStruct($nohide=false, $table=null, $cachepath=null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: this method will always return an array of fields objects, even if only one is found (for more consistency), and this is the main difference with fetchFieldStruct(null).&lt;br /&gt;
&lt;br /&gt;
== Managing custom fields records ==&lt;br /&gt;
&lt;br /&gt;
Now that you know how to create and manage the structure of custom fields, we can proceed on to managing the records.&lt;br /&gt;
&lt;br /&gt;
=== Creating a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Simply use the create() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;create($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
$object must be an object containing as its properties the customfields data.&lt;br /&gt;
&lt;br /&gt;
For example, if you have a field named mycustomfield, your object must have $object-&amp;gt;mycustomfields defined.&lt;br /&gt;
&lt;br /&gt;
This method will store in the database ALL the custom fields that were created/edited and with a valid value (eg: an empty value for a field that does not accept null value is invalid) AT ONCE! So you can modify all the columns of one record at once, but not several records at once.&lt;br /&gt;
&lt;br /&gt;
Concretely, this is very useful with Dolibarr, because modules tends to store everything inside a single object generally called $object, and CustomFields tries to hook onto $object, so that you can just pass $object as is to create() and it will save all the custom fields.&lt;br /&gt;
&lt;br /&gt;
But you can also create an empty object with $object = new stdClass(); and then manually fill the custom fields records with whatever you want, and then call the create() method, this will work as well!&lt;br /&gt;
&lt;br /&gt;
You can also just modify only one column of one record, by just specifying one custom field column_name in your $object. The other custom fields that are not specified won&#039;t be created/modified.&lt;br /&gt;
&lt;br /&gt;
=== Updating a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Use the update() method, which is an alias for the create() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;update($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As noted above, update() is an alias of the create() method, which means that CustomFields will try to detect by itself if a record needs to be updated or created.&lt;br /&gt;
&lt;br /&gt;
=== Deleting a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Use the delete() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;delete($id); // where $id is = to fk_moduleid column, NOT rowid&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fetching a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Just use the fetch() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$records = $customfields-&amp;gt;fetch($id=null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where $id can either be = fk_moduleid (NOT rowid), or null to fetch all the records at once.&lt;br /&gt;
&lt;br /&gt;
This method will either return null, a single record object, or an array of records objects.&lt;br /&gt;
&lt;br /&gt;
=== Fetching all custom fields&#039; records ===&lt;br /&gt;
&lt;br /&gt;
Use the fetchAll() method, which is an alias for fetch():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$records = $customfields-&amp;gt;fetchAll();&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: this method will always return an array of records object, for more consistency (and this is the main difference with fetch(null) ).&lt;br /&gt;
&lt;br /&gt;
=== Fetching referenced records of a constrained field ===&lt;br /&gt;
&lt;br /&gt;
A constrained custom field points to another database table, and a value for this constrained field is the rowid of a record in the referenced table.&lt;br /&gt;
&lt;br /&gt;
Thus, the principal purpose of a constrained custom field is not the value of the field in itself (it&#039;s simply an integer), but rather the other fields available from the referenced table, the constrained field being in fact just a pointer.&lt;br /&gt;
&lt;br /&gt;
To fetch the remote record from the referenced table, use the fetchReferencedValuesList() of the CustomFields class:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$fkrecord = fetchReferencedValuesList($field, $id=null, $where=null, $allcolumns=false);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
&lt;br /&gt;
* $field is a custom field object (fetched using fetchFieldStruct() or fetchAllFieldsStruct()),&lt;br /&gt;
* $id is the rowid of the record to get, just like fetch() and fetchAll(). Can be null to fetch all remote records (or to use the $where clause).&lt;br /&gt;
* $where is a string without the WHERE clause (eg: &amp;quot;fk_facture=1&amp;quot;) to get finer control. In this case, you can use $id or set $id=null depending on your purpose. This is mainly a private argument to allow for cascade option.&lt;br /&gt;
* $allcolumns if false, it will fetch only the required columns to do the smart value substitution. If true, it will fetch all remote columns (useful for ODT substitution). If an array of strings, each string will be a column to fetch from the remote database.&lt;br /&gt;
&lt;br /&gt;
Note that this function is NOT recursive (it will fetch the records from the referenced table, and that&#039;s all, it won&#039;t fetch the custom fields of the referenced table nor recursively fetch the remote constrained custom fields). To do recursive fetching, see in the Facade API.&lt;br /&gt;
&lt;br /&gt;
Note2: this function automatically manages Smart Value Substitution depending of the $field-&amp;gt;column_name.&lt;br /&gt;
&lt;br /&gt;
=== Recursive fetching referenced records of a constrained field ===&lt;br /&gt;
&lt;br /&gt;
If you want to recursively fetch a constrained field (eg: constrainedfield1-&amp;gt;ref_table-&amp;gt;constrainedfield2-&amp;gt;ref_table2-&amp;gt;...), you need another function called fetchReferencedValuesRec(). Technically, this function is in the Facade API (inside /customfields/lib/customfields_aux.lib.php), but it isn&#039;t meant to be used by lambda users (contrary to customfields_fill_object()), but is mainly a helper function for the rest of the facade API.&lt;br /&gt;
&lt;br /&gt;
fetchReferencedValuesRec() works very similarly to fetchReferencedValuesList(), but it will work recursively.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$fkrecord = fetchReferencedValuesRec($customfields, $field, $id, $recursive=true)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where $customfields is the CustomFields instance for the module you are working on, $field is the constrained field you want to fetch, $id is the record&#039;s id you want to fetch, and $recursive enables or disables the recursion.&lt;br /&gt;
&lt;br /&gt;
Note that this function automatically manages Smart Value Substitution (and also recursively).&lt;br /&gt;
&lt;br /&gt;
Note2: This function tries to automatically avoid an infinite recursion loop by using a $blacklist: for a given constrained custom field, a table won&#039;t be visited twice. This means that for example you can do: invoice-&amp;gt;user-&amp;gt;socpeople-&amp;gt;invoice, this will work and every referenced fields will be fetched, but it will automatically stop the recursion at the second invoice, because else it could continue in a loop to user, socpeople, invoice, user, socpeople, etc.&lt;br /&gt;
&lt;br /&gt;
Note3: when adding recursively referenced fields, the key in the returned array will be prefixed by the name of the custom field that led to those remote fields. Thus, you can have something like &amp;quot;cf_myfield1_cf_remotefield1_cf_remotefield2&amp;quot; if you have a constrained field cf_myfield1-&amp;gt;cf_remotefield1-&amp;gt;cf_remotefield2.&lt;br /&gt;
&lt;br /&gt;
== Input forms for custom fields ==&lt;br /&gt;
&lt;br /&gt;
The API provides some neat ways to automatically manage the input forms for users to input new values, with the best adapted HTML form based on the custom field&#039;s data type.&lt;br /&gt;
&lt;br /&gt;
The following methods will print input fields that are best adapted to the SQL data type of your custom fields as well as figure out the possible values and even format them (for example: a not null enum will be printed as a HTML select list without an empty option; fetch automatically remote columns for constrained fields and show them in a select).&lt;br /&gt;
&lt;br /&gt;
So you don&#039;t have to bother how your data will be presented to the user nor how it will be processed.&lt;br /&gt;
&lt;br /&gt;
=== Printing an input form adapted to the field&#039;s data type ===&lt;br /&gt;
&lt;br /&gt;
showInputField() is a sort of converter customfieldstructure -&amp;gt; HTML input field.&lt;br /&gt;
&lt;br /&gt;
Use the &#039;&#039;&#039;showInputField()&#039;&#039;&#039; method to print the right input form for a custom field.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;showInputField($field, $currentvalue=null, $moreparam=&#039;&#039;, $ajax_php_callback=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
showInputField() takes 4 parameters:&lt;br /&gt;
&lt;br /&gt;
* $field is a CustomFields structure object (eg: returned by fetchFieldStruct()) and is necessary to detect the right datatype and the possible values.&lt;br /&gt;
* $currentvalue is the current value if you want to preselect a value (for example if the user already defined a value, you can reload it here). This is optional, in case it is not defined, either a null/empty value will be chosen, or either the default value defined when creating the custom field.&lt;br /&gt;
* $moreparam is used to add more HTML attributes, if you want for example to add some css styling or whatever you want.&lt;br /&gt;
* $ajax_php_callback is optional and allows to specify the relative url to the php callback script. If specified, an AJAX script will be automatically attached to this field (see AJAX-PHP callback below).&lt;br /&gt;
&lt;br /&gt;
showInputField() only prints ONE HTML input field, thus you have to enclose it/them by your own HTML form.&lt;br /&gt;
&lt;br /&gt;
eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print &#039;&amp;lt;form method=&amp;quot;get&amp;quot; action=&amp;quot;?&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
print &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;customtoken&amp;quot; value=&amp;quot;ABCDEFGH&amp;quot; /&amp;gt;&#039;; // example of a custom hidden HTML input field, that can be used to pass a token for example&lt;br /&gt;
// Loop over each custom fields (you have to load them before with $customfields-&amp;gt;fetchAllFieldsStruct()&lt;br /&gt;
foreach ($customfields-&amp;gt;fields as $field) {&lt;br /&gt;
    print $customfields-&amp;gt;showInputField($field, $currentvalue=null, $moreparam=&#039;&#039;);&lt;br /&gt;
}&lt;br /&gt;
print &#039;&amp;lt;/form&amp;gt;&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can also store the resulting HTML input field and process it by a regexp for example:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$myhtmlfield = $customfields-&amp;gt;showInputField($field, $currentvalue=null, $moreparam=&#039;&#039;);&lt;br /&gt;
preg_replace($myhtmlfield, ..., ...);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: for constrained fields, if the name follows the convention of &#039;&#039;&#039;Smart Value Substitution&#039;&#039;&#039; (ie: remote columns names are included in the custom field&#039;s name), showInputField() will &#039;&#039;&#039;automatically fetch the remote values, format them, and present them&#039;&#039;&#039; in a HTML select.&lt;br /&gt;
&lt;br /&gt;
=== Printing an input form for a custom field ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;showInputForm()&#039;&#039;&#039; is a function used to print a single (and only one!) custom field with an adapted HTML input field, but enclosed by a HTML form. This is used for example when you want to allow to edit a single custom field but not the others.&lt;br /&gt;
&lt;br /&gt;
showInputForm() is in fact a wrapper for showInputField(), thus it will call showInputField() and simply enclose it by a form.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;showInputForm($id, $field, $currentvalue=null, $idvar=&#039;id&#039;, $page=null, $moreparam=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== AJAX-PHP callback ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;showInputFieldAjax()&#039;&#039;&#039; will output an AJAX script (using jQuery) to send and receive data to and from a specified php script. The AJAX script will automatically manage HTML form input&#039;s updating as long as the received data follows some convention.&lt;br /&gt;
&lt;br /&gt;
An AJAX script can be attached to any field (including non-CustomFields) by using the method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;showInputFieldAjax($id, $php_callback_url, $on_func=&amp;quot;change&amp;quot;, $request_type=&amp;quot;post&amp;quot;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
* $id is the HTML id of the field you want to attach the AJAX to.&lt;br /&gt;
* $phpcallback the relative path from Dolibarr&#039;s htdocs root folder to the php script that will receive and send back data.&lt;br /&gt;
* $on_func is the Javascript or jQuery event that should trigger the AJAX script (by default on change).&lt;br /&gt;
* $request_type is the HTML query type that will be used to send the data to the PHP callback (either &amp;quot;get&amp;quot; or &amp;quot;post&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
The AJAX script will automatically manage sending/receiving of data:&lt;br /&gt;
&lt;br /&gt;
* automatically send all form&#039;s inputs values, as well as the current calling field&#039;s name and value. The sending happens on change by default, but another event can be set using the $on_event argument. Data will be sent using standard GET or POST.&lt;br /&gt;
* Upon reception of data from the php script, the AJAX script will automatically parse the data and update the HTML fields.&lt;br /&gt;
&lt;br /&gt;
The PHP callback script must send back to AJAX the data in JSON encoded format, and with with a specific format:&lt;br /&gt;
&lt;br /&gt;
* the data must be an associative array, where each entry&#039;s key is the name (not ID!) of an HTML field to update.&lt;br /&gt;
* each entry&#039;s value is an associative array, where each sub-entry&#039;s key is the action to do. Available actions: &#039;options&#039;, &#039;value&#039;, &#039;html&#039;, &#039;alert&#039;.&lt;br /&gt;
* each sub-entry&#039;s value is the value for this action and HTML field.&lt;br /&gt;
&lt;br /&gt;
Thus, this approach allows to generically support a wide range of updating actions, managed automatically.&lt;br /&gt;
&lt;br /&gt;
You, the implementer, have just to edit the PHP callback script and return an associative array defining what action you want for each field you want to update. This is exactly what has been implemented in the Custom AJAX Functions library, which simplifies even more your job by managing automatically the step described here (generating AJAX and formatting data), so that you can focus on &#039;&#039;&#039;making the data&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Printing custom fields in documents ==&lt;br /&gt;
&lt;br /&gt;
The API can also automatically manage the printing and formatting of custom fields in your ODT, PDF and HTML documents.&lt;br /&gt;
&lt;br /&gt;
=== Printing in HTML ===&lt;br /&gt;
&lt;br /&gt;
You can either just print the value as-is, or pass them through the printField() or simpleprintField() function to format them (very useful for constrained fields) - see below.&lt;br /&gt;
&lt;br /&gt;
=== Printing in documents ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;printField()&#039;&#039;&#039; is used to properly print and translate a custom field&#039;s record&#039;s value. Generally, you can just print the custom field&#039;s value as-is, but in a few cases (eg for boolean types, or constrained fields), using the printField() method will more properly print the value.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;printField($field, $value, $outputlangs=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
* $field is the custom field&#039;s SQL structure as an object (eg: returned by fetchFieldStruct()).&lt;br /&gt;
* $value is the custom field&#039;s value for the current record (eg: $customfields-&amp;gt;fields-&amp;gt;myfield).&lt;br /&gt;
* $outputlangs is the Translate object for the target language.&lt;br /&gt;
&lt;br /&gt;
For example, with a constrained field with Smart Value Substitution, instead of printing the id like 1, it will print the full name of user like FirstName Name.&lt;br /&gt;
&lt;br /&gt;
This method will also translate a value if a translation is found in the language files.&lt;br /&gt;
&lt;br /&gt;
Another method &#039;&#039;&#039;printFieldPDF()&#039;&#039;&#039; can be used to properly print a value for a PDF (properly encoding the characters).&lt;br /&gt;
&lt;br /&gt;
Powertip: this function can also be used to get the Smart Value Substituted value of the field (instead of printing the rowid) in the field is constrained.&lt;br /&gt;
&lt;br /&gt;
=== Simple printing in documents ===&lt;br /&gt;
&lt;br /&gt;
If you don&#039;t want to have to manually fetch the structure of every custom field whose you want to print the value, you can then use a simplifier method called &#039;&#039;&#039;simpleprintField()&#039;&#039;&#039; which will take as input a custom field name instead of a custom field SQL structure object.&lt;br /&gt;
&lt;br /&gt;
This method is just a simplifier (Facade) wrapper for the printField() method, and thus will have the same advantages (fetch constrained fields, values translation, etc.).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;simpleprintField($fieldname, $value, $outputlangs=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
* $fieldname is the custom field&#039;s name (eg: &amp;quot;myfield&amp;quot;).&lt;br /&gt;
* $value is the custom field&#039;s value for the current record (eg: $customfields-&amp;gt;fields-&amp;gt;myfield).&lt;br /&gt;
* $outputlangs is the Translate object for the target language.&lt;br /&gt;
&lt;br /&gt;
Another method &#039;&#039;&#039;simpleprintFieldPDF()&#039;&#039;&#039; can be used to properly print a value for a PDF (properly encoding the characters).&lt;br /&gt;
&lt;br /&gt;
Powertip: this function can also be used to get the Smart Value Substituted value of the field (instead of printing the rowid) in the field is constrained.&lt;br /&gt;
&lt;br /&gt;
=== Find the label ===&lt;br /&gt;
&lt;br /&gt;
Finding the label of a custom field might not be as simple as it seems. First, values of properties objects may be easily used, but their name (key) is a worst job. Secondly, custom fields&#039; labels can be translated, and the following functions are expressly made for that purpose. Thirdly, for PDF and some other documents you have to encode the characters if you want them to properly be drawn.&lt;br /&gt;
&lt;br /&gt;
So if you want to print a custom field&#039;s label with translation (multilingual) support, just use the &#039;&#039;&#039;findLabel()&#039;&#039;&#039; method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$translatedfieldname = $customfields-&amp;gt;findLabel($fieldname, $outputlangs = &#039;&#039;); // where $fieldname is just a custom field label (eg: cf_mycustomfield or mycustomfield, both will work) and $outputlangs is the target language object (see the Translate class native of Dolibarr)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As for the other printing methods, you also have the &#039;&#039;&#039;findLabelPDF()&#039;&#039;&#039; wrapper method to easily print labels that with characters encoded for PDF.&lt;br /&gt;
&lt;br /&gt;
= Facade API =&lt;br /&gt;
&lt;br /&gt;
The Facade API was made to ease the usage of the CustomFields class with the Dolibarr system. It provides a neat way to do many operations with single commands, and will automatically manage the loading of the CustomFields class, and its usage with many optimizations.&lt;br /&gt;
&lt;br /&gt;
== customfields_fill_object() ==&lt;br /&gt;
&lt;br /&gt;
You can load the Facade API and then use the &#039;&#039;&#039;customfields_fill_object()&#039;&#039;&#039; to automatically fetch the fields structures and the records, and also automatically link to remote fields when a constrained field is found. This is an all-in-one solution to easily fetch CustomFields datas, and is also optimized to fully use caching and other special properties of the CustomFields class.&lt;br /&gt;
&lt;br /&gt;
Concretely, you just have to include the Facade API, then pass the customfields_fill_object() with a Dolibarr $object, and the Facade API will return a CustomFields object with appended the custom fields records and also the custom fields&#039; SQL structures.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$customfields = customfields_fill_object($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* $object will then include the custom fields records associated with this object (eg: $object-&amp;gt;customfields-&amp;gt;cf_yourfield).&lt;br /&gt;
* $customfields will be a CustomFields object containing the fields structures (eg: $customfields-&amp;gt;fields-&amp;gt;yourfield).&lt;br /&gt;
&lt;br /&gt;
You can also call any CustomFields function if you need to using the $customfields object, eg: $customfields-&amp;gt;simpleprintField($object-&amp;gt;customfields-&amp;gt;cf_yourfield);&lt;br /&gt;
&lt;br /&gt;
Note: $object can also be a dummy object containing only two properties that are required: $object-&amp;gt;id and $object-&amp;gt;table_element.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will now describe the full specification of this function:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$customfields = customfields_fill_object(&amp;amp;$object,$fromobject = null, $outputlangs = null, $prefix = null,$pdfformat = false);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* $object is the object where the customfields will be saved&lt;br /&gt;
* $fromobject is the object from where the customfields will be fetched (this can be a different module than $object, or can be null to be the same as $object)&lt;br /&gt;
* $outputlangs is the target language for translation of fields value (if empty, the default current global translation object will be used)&lt;br /&gt;
* $prefix allows you to save the custom fields in a sub-object. By default, when $prefix=null, custom fields are saved in the path $object-&amp;gt;customfields, but when you specify a prefix, custom fields will be saved inside $object-&amp;gt;customfields-&amp;gt;$prefix. This may be useful if you want to store multiple object&#039;s custom fields or records in the same $object.&lt;br /&gt;
* $pdfformat defines if you want a special formatting for the values: if null, there&#039;s no formatting, if false, there is a simple formatting (eg: translation of fields&#039; values), if true then the formatting will be adapted for PDF templating.&lt;br /&gt;
&lt;br /&gt;
For more informations on pratical usages of this function, please [[Module_CustomFields#Implementing_in_php_code_.28dolibarr_core_modules_or_your_own_module.29|read the user documentation on how to implement CustomFields in php code]] or [[Module_CustomFields_Cases#Linking_Dolibarr_objects_from_two_different_modules|read an example case of linking two objects of different modules together]].&lt;br /&gt;
&lt;br /&gt;
== customfields_fill_object_lines() ==&lt;br /&gt;
&lt;br /&gt;
Similar to customfields_fill_object() for normal custom fields, there is also a &#039;&#039;&#039;customfields_fill_object_lines()&#039;&#039;&#039; to fetch custom fields associated with lines of products/services. This function is also optimized and it is strongly advised to use it and to not try to fetch manually this kind of custom fields (ie: associated with lines of products/services rather than objects), because there are a lot of optimizations going on behind the scene and that are very hard to do manually.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields for lines of products/services (you can then access them by doing $object-&amp;gt;customfields-&amp;gt;lines-&amp;gt;$lineid-&amp;gt;cf_yourfield)&lt;br /&gt;
$customfields = customfields_fill_object_lines($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Requirements: $object can be a dummy object but must contain at least 2 properties: $object-&amp;gt;id and $object-&amp;gt;table_element_line.&lt;br /&gt;
&lt;br /&gt;
For more informations on pratical usages of this function, please [[Module_CustomFields#Implementing_in_php_code_.28dolibarr_core_modules_or_your_own_module.29|read the user documentation on how to implement CustomFields in php code]].&lt;br /&gt;
&lt;br /&gt;
= Architecture =&lt;br /&gt;
Here is a full list of the CustomFields packaged files with a short description (for a more in-depth view just crawl the source files, they are full of comments):&lt;br /&gt;
&lt;br /&gt;
== Core files ==&lt;br /&gt;
files that are necessary for the CustomFields to work, they contains the core functions&lt;br /&gt;
&lt;br /&gt;
* /customfields/admin/customfields.php --- Administrator&#039;s configuration panel : this is where you create and manage the custom fields definitions&lt;br /&gt;
* /customfields/class/actions_customfields.class.php --- Hooks class : used to hook into Dolibarr core modules without altering any core file (can be used to hook into your own modules too). Also used to preprocess the parameters before calling the printforms library.&lt;br /&gt;
* /customfields/class/customfields.class.php --- Core class : every database action is made here in this class. You can find some printing functions because they are very generic.&lt;br /&gt;
* /customfields/conf/conf_customfields.lib.php --- Configuration file : contains the main configurable variables to adapt CustomFields to your needs or to expand its support to other modules and more native sql types.&lt;br /&gt;
* /customfields/conf/conf_customfields_func.lib.php --- Auxiliary functions library to read and manage config variables&lt;br /&gt;
* /customfields/core/modules/modCustomFields.class.php --- Dolibarr&#039;s module definition file : this is a core file necessary for Dolibarr to recognize the module and to declare the hooks to Dolibarr (but it does not store anything else than meta-informations).&lt;br /&gt;
* /customfields/core/substitutions/functions_customfields.lib.php --- CustomFields substitution class for ODT generation : necessary to support customfields tags in ODT files&lt;br /&gt;
* /customfields/core/triggers/interface_50_modCustomFields_SaveFields.class.php --- Core triggers file : this is where the actions on records are managed. This is an interface between other modules and CustomFields management. You should not modify this file but you should add your triggers in $triggersarray inside conf_customfields.lib.php, but if you really have a special need and know what you do, you can edit this file to directly add your own triggers. Also, if CustomFields are shown in the forms but cannot be modified nor saved, then probably this trigger file is not detected by Dolibarr (maybe you can try to lower the number 50 to raise the priority?).&lt;br /&gt;
* /customfields/fields/customfields_fields_extend.lib.php.example --- Overloading functions library with users functions: this is where implementers can use their own php code to manage the custom fields they want (one function per action and per field).&lt;br /&gt;
* /customfields/fields/customfields_fields_ajax_custom.lib.php.example --- Custom AJAX functions library with users functions: this is where implementers can use their own php code to manage the custom fields cascading and AJAX calls, similarly to the Overloading functions library (one function per action and per field).&lt;br /&gt;
* /customfields/langs/code_CODE/customfields.lang --- Core language file : this is where you can translate the admin config panel (data types names, labels, descriptions, etc.)&lt;br /&gt;
* /customfields/langs/code_CODE/customfields-user.lang --- User defined language file : this is where you can store the labels and values of your custom fields (see the related chapter)&lt;br /&gt;
* /customfields/lib/customfields_aux.lib.php --- Simplifier library to easily use CustomFields class to populate an $object with custom fields datas (Facade design pattern). This is what you should use for most of your needs.&lt;br /&gt;
* /customfields/lib/customfields_printforms.lib.php --- Core printing library for records : contains only printing functions, there&#039;s not really any core functions inside but it allows to manage the printing of the custom fields records and their editing.&lt;br /&gt;
* /customfields/lib/customfields_ajax_wrapper.lib.php --- AJAX wrapper library to automatically manage cascading at objects creation. This isn&#039;t really necessary for CustomFields to function, this is more an addition to allow dynamical AJAX updating, but CustomFields core functionalities can work without this library.&lt;br /&gt;
* /customfields/sql/*.sql --- Unused (the tables are created directly via a function in the customfields.class.php) - but the sql files are still valid, they were used as squeletton in the first designs.&lt;br /&gt;
&lt;br /&gt;
== PDFTest module ==&lt;br /&gt;
optional module to append a page listing all custom fields values on newly generated PDF documents. To be used to test if custom fields are working correctly before making one&#039;s own template.&lt;br /&gt;
&lt;br /&gt;
* /customfieldspdftest/class/actions_customfieldspdftest.class.php --- Main functions library and hooks class, to hook onto pdf generation. Contains the PDFTest functions.&lt;br /&gt;
* /customfieldspdftest/core/modules/modCustomFieldsPDFTest.class.php --- Module descriptor for Dolibarr&lt;br /&gt;
&lt;br /&gt;
== Runtime outline ==&lt;br /&gt;
&lt;br /&gt;
Here is an outline of how CustomFields works: what functions will be called and why:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At admin page:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* No external function call (apart for some icons or needed admin functions like for tabs), everything that is done are sql queries via CustomFields class (to fetch and modify the sql columns in the customfields tables).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At printing:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Module&#039;s hook formObjectOptions calls Dolibarr&#039;s hookmanager&lt;br /&gt;
* Hookmanager calls CustomFields /class/actions_customfields.class.php, function formObjectOptions() of CustomFields&lt;br /&gt;
* Preprocess a few parameters (mainly the $action) and calls customfields_print_forms()&lt;br /&gt;
* customfields_print_forms() preprocess all the hook&#039;s parameters and assimilate them with the CustomFields&#039;s configuration parameters (there&#039;s no connection to the database at this stage, only parameters preprocessing), then calls /lib/customfields_printforms.lib.php, functions customfields_print_creation_form() or customfields_print_datasheet_form&lt;br /&gt;
* both customfields_print_creation_form() lookup the database using CustomFields class and prints empty custom fields; customfields_print_datasheet_form() lookup the database using CustomFields class, fetch the values of the custom fields and prints them;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At edition:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Same steps as &#039;&#039;&#039;At printing&#039;&#039;&#039;&lt;br /&gt;
* then customfields_print_datasheet_form() process the custom field being edited differently than the others: if editing, it prints the html input field (to enter a new value), or if saving it just saves the data into the database by calling the CUSTOMFIELDS_MODIFY trigger and then continue to print the other fields normally.&lt;br /&gt;
* CUSTOMFIELDS_MODIFY trigger calls $customfields-&amp;gt;update($newrecord);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At creation (saving):&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Module&#039;s creation trigger MYMODULE_CREATE calls Dolibarr&#039;s run_trigger()&lt;br /&gt;
* Dolibarr run_trigger() calls CustomFields trigger file /core/trigger/interface_50_modCustomFields_SaveFields.class.php, function run_trigger()&lt;br /&gt;
* CustomFields&#039;s run_trigger() assimilate the given parameters with the CustomFields&#039;s config parameters, and if the trigger is found in $triggersarray, then the appropriate action is taken (by default: _CREATE or another standard action if recognized), if not in $triggersarray, the function will still try to automatically detect the appropriate action by detecting MYMODULE_ALISTOFACTIONS with a regular expression.&lt;br /&gt;
* The appropriate action is then done (creation in this case) by calling $customfields-&amp;gt;create($record).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At edition or creation of product lines:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Similar to &#039;&#039;&#039;At edition&#039;&#039;&#039; but with hooks: formCreateProductOptions and formEditProductOptions.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At ODT document generation and substitution:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Module&#039;s ODT generator calls for Dolibarr&#039;s function complete_substitution_array(), which then calls for CustomFields substitution file /core/substitution/functions_customfields.lib.php, function customfields_completesubstitutionarray()&lt;br /&gt;
* CustomFields&#039;s function customfields_completesubstitutionarray() calls the simplifier library customfields_aux.lib.php, function customfields_fill_object() and make the substitution inside the $substitutionarray.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At ODT document generation and substitution for product lines:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Same as the previous point but this function is called for every product lines, and thus the data is cached. Also the simplifier function is here called customfields_fill_object_lines(), and the CustomFields&#039;s substitution function called customfields_completesubstitutionarray_lines().&lt;br /&gt;
&lt;br /&gt;
= How to add the support of a new module =&lt;br /&gt;
&lt;br /&gt;
There are two cases: either the module already implements hooks, and you just have to configure CustomFields to support it, or either the module does not implements hooks nor triggers.&lt;br /&gt;
&lt;br /&gt;
This chapter will guide you through the steps to make any module supported by CustomFields (be it a Dolibarr core module or your own module).&lt;br /&gt;
&lt;br /&gt;
== Preliminary test ==&lt;br /&gt;
&lt;br /&gt;
This preliminary test will allow you to know at a glance how much work needs to be done in order to make your module supported by CustomFields, and it will also give you all the config parameters if they are available.&lt;br /&gt;
&lt;br /&gt;
Important note: this test will only give data about hooks, which is the main part to make CustomFields work for your module. But for triggers, there is no way to test, so you will have to check the triggers manually (either by testing if the custom fields are saved at creation page, or by looking at the source code of the objects classes).&lt;br /&gt;
&lt;br /&gt;
Edit the php script of the module (check the URL when you access the module&#039;s datasheet or creation page - attention both are two different pages, so you have to do this twice), then put this at the very &#039;&#039;&#039;end&#039;&#039;&#039; of the php script:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// CustomFields debuglines to show config parameters for current module&lt;br /&gt;
print(&amp;quot;&amp;lt;pre&amp;gt;&amp;quot;); // html nice output&lt;br /&gt;
print(&amp;quot;table_element: {$object-&amp;gt;table_element} - table_element_line: {$object-&amp;gt;table_element_line} - contexts: &amp;quot;);&lt;br /&gt;
print_r($hookmanager-&amp;gt;contextarray);&lt;br /&gt;
print(&amp;quot; - conf hooks_modules: &amp;quot;);&lt;br /&gt;
print_r($conf-&amp;gt;hooks_modules);&lt;br /&gt;
print(&amp;quot; - hooks attached: &amp;quot;);&lt;br /&gt;
print_r($hookmanager-&amp;gt;hooks);&lt;br /&gt;
print(&amp;quot; - user rights: &amp;quot;);&lt;br /&gt;
print_r($user-&amp;gt;rights);&lt;br /&gt;
print(&amp;quot;&amp;lt;/pre&amp;gt;&amp;quot;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now reload the page, and scroll at the very bottom. You should see some informations about the module.&lt;br /&gt;
&lt;br /&gt;
Depending on the data available, you will know what you have to do:&lt;br /&gt;
&lt;br /&gt;
* If all the datas is available, then it&#039;s ok, jump to [[Module_CustomFields_Dev#Configuring_CustomFields_to_support_the_module]] and copy these datas to the parameters of conf_customfields.lib.php&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;table_element&#039;&#039;&#039;: $object is not defined, try to find how is called the main object variable and replace $object by this variable in the debug code given above (eg: in Agenda, the object variable is called $agenda and $agendacomm).&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;table_element_line&#039;&#039;&#039;: either there&#039;s no products lines for this module, either the main object variable is not called $object (see the previous point). One last case is that the object is not standard and does not store table_element_line (the name of the table that stores its products lines), in which case you have to manually add it in the class (eg: $this-&amp;gt;table_element_line = &#039;something&#039;).&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;contexts&#039;&#039;&#039;: the hookmanager is not defined, and probably there&#039;s no hook at all in this module, so you&#039;ll have to implement them by yourself. Jump to [[Module_CustomFields_Dev#Hooks]].&lt;br /&gt;
&lt;br /&gt;
* If there is a list of &#039;&#039;&#039;contexts&#039;&#039;&#039; but the module cannot be found in the list: the hookmanager is defined but the module has not defined a context, you still have to implement the hooks. Jump to [[Module_CustomFields_Dev#Hooks]].&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;conf hooks_modules&#039;&#039;&#039;: this variable is not necessary, empty or not it doesn&#039;t matter. But if you see customfields and/or customfieldspdftest inside the list, it is a confirmation that CustomFields indeed is hooking the module correctly.&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;hooks attached&#039;&#039;&#039;, or CustomFields cannot be found in the list: CustomFields has not been properly configured to hook to this module, or you forgot to disable then re enable CustomFields in order to update the CustomFields&#039;s hooks. Jump to [[Module_CustomFields_Dev#Configuring_CustomFields_to_support_the_module]].&lt;br /&gt;
&lt;br /&gt;
* If there is no &#039;&#039;&#039;$user-&amp;gt;rights&#039;&#039;&#039;: there is either no standard security implemented in this module, or either the variable is called with another name. If there is no security, you don&#039;t need to configure it in CustomFields, but if there is a security, you need to find how the variable is called (like $object can get another name sometimes).&lt;br /&gt;
&lt;br /&gt;
* If there is &#039;&#039;&#039;$user-&amp;gt;rights&#039;&#039;&#039; but the module can&#039;t be found in the list: either the name for the rights is different (this often happens, the rights array is not yet standardized), or either the rights are hardcoded into the module&#039;s code (rights should be announced to the $user-&amp;gt;rights array, but sometimes it happens that the dev forgot to do so, and thus the right is entirely hardcoded and you can&#039;t find it in the dynamic rights array). In any of these two cases, you will have to manually read the module&#039;s code to find the right. Also, maybe there is no security implemented, but since $user-&amp;gt;rights is declared this is less probable.&lt;br /&gt;
&lt;br /&gt;
Here is an example of output on a correctly hooked module (invoice):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
table_element: facture - table_element_line: facturedet - contexts: Array&lt;br /&gt;
(&lt;br /&gt;
    [0] =&amp;gt; searchform&lt;br /&gt;
    [1] =&amp;gt; leftblock&lt;br /&gt;
    [2] =&amp;gt; toprightmenu&lt;br /&gt;
    [3] =&amp;gt; invoicecard&lt;br /&gt;
)&lt;br /&gt;
 - conf hooks_modules: Array&lt;br /&gt;
(&lt;br /&gt;
    [customfields] =&amp;gt; invoicecard:propalcard:productcard:ordercard:thirdpartycard:contactcard:ordersuppliercard:invoicesuppliercard:membercard:actioncard:projectcard:projecttaskcard:contractcard:interventioncard:doncard:tripsandexpensescard:taxvatcard:expeditioncard:invoicecard:propalcard:ordercard:ordersuppliercard:invoicesuppliercard&lt;br /&gt;
    [customfieldspdftest] =&amp;gt; Array&lt;br /&gt;
        (&lt;br /&gt;
            [0] =&amp;gt; pdfgeneration&lt;br /&gt;
            [1] =&amp;gt; ordersuppliercard&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
)&lt;br /&gt;
 - hooks attached: Array&lt;br /&gt;
(&lt;br /&gt;
    [invoicecard] =&amp;gt; Array&lt;br /&gt;
        (&lt;br /&gt;
            [customfields] =&amp;gt; ActionsCustomFields Object&lt;br /&gt;
                (&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
)&lt;br /&gt;
 - user rights: stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [user] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [user] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [lire] =&amp;gt; 1&lt;br /&gt;
                    [creer] =&amp;gt; 1&lt;br /&gt;
                    [password] =&amp;gt; 1&lt;br /&gt;
                    [supprimer] =&amp;gt; 1&lt;br /&gt;
                    [export] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [self] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [creer] =&amp;gt; 1&lt;br /&gt;
                    [password] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [user_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [readperms] =&amp;gt; 1&lt;br /&gt;
                    [write] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [self_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [readperms] =&amp;gt; 1&lt;br /&gt;
                    [writeperms] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [group_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [read] =&amp;gt; 1&lt;br /&gt;
                    [readperms] =&amp;gt; 1&lt;br /&gt;
                    [write] =&amp;gt; 1&lt;br /&gt;
                    [delete] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    [facture] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [lire] =&amp;gt; 1&lt;br /&gt;
            [creer] =&amp;gt; 1&lt;br /&gt;
            [invoice_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [unvalidate] =&amp;gt; 1&lt;br /&gt;
                    [send] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [valider] =&amp;gt; 1&lt;br /&gt;
            [paiement] =&amp;gt; 1&lt;br /&gt;
            [supprimer] =&amp;gt; 1&lt;br /&gt;
            [facture] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [export] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    [propale] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [lire] =&amp;gt; 1&lt;br /&gt;
            [creer] =&amp;gt; 1&lt;br /&gt;
            [valider] =&amp;gt; 1&lt;br /&gt;
            [propal_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [send] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [cloturer] =&amp;gt; 1&lt;br /&gt;
            [supprimer] =&amp;gt; 1&lt;br /&gt;
            [export] =&amp;gt; 1&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    [produit] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [lire] =&amp;gt; 1&lt;br /&gt;
            [creer] =&amp;gt; 1&lt;br /&gt;
            [supprimer] =&amp;gt; 1&lt;br /&gt;
            [export] =&amp;gt; 1&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    etc... a lot of other rights...&lt;br /&gt;
&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When you are finished with implementing the support of CustomFields in the module, you should delete these debug lines.&lt;br /&gt;
&lt;br /&gt;
== Implementing the support of CustomFields in the module ==&lt;br /&gt;
&lt;br /&gt;
If there is a module you want to implement the support of CustomFields, but does not yet has the required components, here is a guide on how to implement these components.&lt;br /&gt;
&lt;br /&gt;
=== Hooks ===&lt;br /&gt;
&lt;br /&gt;
Implementing hooks will allow custom fields to appear on your module&#039;s page. They will also be editable and they will save the data. For creation, you need to use a trigger (see the subchapter below).&lt;br /&gt;
&lt;br /&gt;
First you should be familiar with the way that hooks are implemented in modules. If you don&#039;t, please first [[Hooks_system#Implementation|read the hooks documentation]].&lt;br /&gt;
&lt;br /&gt;
An example case of implementation of hooks into a Dolibarr core file can be found in the [[Module_CustomFields_Cases|CustomFields Example Cases wiki page]].&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Datasheet hook&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
To implement CustomFields in your module, you must initialize the hook manager, then use the following hook context:&lt;br /&gt;
* formObjectOptions&lt;br /&gt;
&lt;br /&gt;
This hook must be placed everywhere in your code where you want custom fields to appear (eg: on the creation page and datasheet view page).&lt;br /&gt;
&lt;br /&gt;
Note: Generally, you will have to place the hook just before the end of a table tag (&#039;&#039;&#039;&amp;lt;/table&amp;gt;&#039;&#039;&#039;), so if you don&#039;t know where to place it (eg: if you&#039;re implementing CustomFields on a Dolibarr core module), just search for this tag in the page shown in the url (eg: /htdocs/commande/fiche.php).&lt;br /&gt;
&lt;br /&gt;
Eg: First load the hookmanager at the beginning of your php script:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Initialize technical object to manage hooks of thirdparties. Note that conf-&amp;gt;hooks_modules contains array array&lt;br /&gt;
include_once(DOL_DOCUMENT_ROOT.&#039;/core/class/hookmanager.class.php&#039;);&lt;br /&gt;
$hookmanager=new HookManager($db);&lt;br /&gt;
$hookmanager-&amp;gt;initHooks(array(&#039;yourmodule&#039;));&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then place the hook as many times as required:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// ... a lot of php code ...&lt;br /&gt;
&lt;br /&gt;
print &#039;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&#039;; // end of the last row of a table&lt;br /&gt;
&lt;br /&gt;
// Other attributes&lt;br /&gt;
$parameters=array(&#039;colspan&#039; =&amp;gt; &#039; colspan=&amp;quot;3&amp;quot;&#039;); // specifying the colspan is optional but will help CustomFields know how to correctly print the table&lt;br /&gt;
$reshook=$hookmanager-&amp;gt;executeHooks(&#039;formObjectOptions&#039;,$parameters,$object,$action);    // Note that $action and $object may have been modified by hook&lt;br /&gt;
&lt;br /&gt;
print &#039;&amp;lt;/table&amp;gt;&#039;; // end of the table, this is what you&#039;re looking for, place the hook just above&lt;br /&gt;
&lt;br /&gt;
// ... a lot of php code ... redo the same if you find another &amp;lt;/table&amp;gt; and there&#039;s a page where there&#039;s no custom fields but you want to place custom fields on there&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note2: If after implementing the hook you receive an error or custom fields don&#039;t appear, you may have to change $object to another variable, depending on what is called the main object (eg: in Agenda it&#039;s called $action and $actioncomm).&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Lines hooks&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
For product lines, you should anyway use the product lines template files: htdocs/core/tpl/freeproductline_edit.tpl.php&lt;br /&gt;
&lt;br /&gt;
If you don&#039;t, you can still manually implement the required hooks, what is required is that you supply $line (which is the current line object being processed) in the $parameters array:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$parameters=array(&#039;line&#039;=&amp;gt;$line,&#039;fk_parent_line&#039;=&amp;gt;$line-&amp;gt;fk_parent_line);&lt;br /&gt;
&lt;br /&gt;
echo $hookmanager-&amp;gt;executeHooks(&#039;formEditProductOptions&#039;,$parameters,$this,$action);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hooks names:&lt;br /&gt;
* formEditProductOptions&lt;br /&gt;
* formCreateProductOptions ($line is not required here, because we create a new product line! you can leave $parameters=array(); empty).&lt;br /&gt;
Note: there&#039;s no hook for viewing, because showing the customfields will clutter the visual field, but if you really want you can also do it by adding a formViewProductOptions hook.&lt;br /&gt;
&lt;br /&gt;
=== Triggers ===&lt;br /&gt;
&lt;br /&gt;
First you need to know how to implement triggers in Dolibarr. For more informations about how to implement triggers in your module, please read the [[Triggers#Manage_and_create_a_new_trigger.27s_action|triggers wiki page]].&lt;br /&gt;
&lt;br /&gt;
An example case of implementation of hooks into a Dolibarr core file can be found in the [[Module_CustomFields_Cases|CustomFields Example Cases wiki page]].&lt;br /&gt;
&lt;br /&gt;
Implementing triggers on your module will allow CustomFields to save the fields at creation (datasheet edition is already managed by hooks).&lt;br /&gt;
&lt;br /&gt;
You need to implement 3 triggers:&lt;br /&gt;
- on creation of object - eg: YOURMODULE_CREATE&lt;br /&gt;
- on creation of lines - eg: YOURMODULELINE_INSERT (optional, only if your module has product lines)&lt;br /&gt;
- on update of lines - eg: YOURMODULELINE_UPDATE (optional, only if your module has product lines)&lt;br /&gt;
&lt;br /&gt;
Note: you need to supply the sql id of the newly created object before calling the trigger.&lt;br /&gt;
&lt;br /&gt;
Eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$this-&amp;gt;id = $this-&amp;gt;db-&amp;gt;last_insert_id(MAIN_DB_PREFIX.&amp;quot;yourmodule_table_element&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
// Appel des triggers&lt;br /&gt;
include_once(DOL_DOCUMENT_ROOT . &amp;quot;/core/class/interfaces.class.php&amp;quot;);&lt;br /&gt;
$interface=new Interfaces($this-&amp;gt;db);&lt;br /&gt;
$result=$interface-&amp;gt;run_triggers(&#039;YOURMODULE_CREATE&#039;,$this,$user,$langs,$conf);&lt;br /&gt;
if ($result &amp;lt; 0) {&lt;br /&gt;
    $error++; $this-&amp;gt;errors=$interface-&amp;gt;errors;&lt;br /&gt;
}&lt;br /&gt;
// Fin appel triggers&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note2: if it&#039;s a Dolibarr core module, you can easily find the right place to put the trigger by searching for &#039;create sql&#039; in the class subfolder (eg: URL is /htdocs/commande/fiche.php, the class is in /htdocs/commande/class/commande.class.php).&lt;br /&gt;
&lt;br /&gt;
Note3: CustomFields also supports a few other triggers like _CLONE, _UPDATE, _MODIFY, but they are not required and may not work depending on how your module works to manage these actions.&lt;br /&gt;
&lt;br /&gt;
== Configuring CustomFields to support the module ==&lt;br /&gt;
&lt;br /&gt;
=== Preliminary work ===&lt;br /&gt;
&lt;br /&gt;
You need to get the parameters of this module, and you will then put them in the CustomFields configuration file (conf_customfields.lib.php).&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Getting the parameters automatically&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
You can use the [[Module_CustomFields_Dev#Preliminary_test|preliminary test]] to easily get all those parameters.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Getting the parameters manually&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
To configure CustomFields to support a module, you need at least 5 parameters:&lt;br /&gt;
* the module&#039;s table_name (name of the sql table where the module stores its datas)&lt;br /&gt;
* the module&#039;s context (name given by the module to the hookmanager)&lt;br /&gt;
* the module&#039;s idvar (name of the variable that holds the sql row id of the record)&lt;br /&gt;
* the module&#039;s trigger action(s) (triggers on creation and update, see the related chapter above)&lt;br /&gt;
* the module&#039;s rights (for security purposes)&lt;br /&gt;
&lt;br /&gt;
To get these parameters manually, you can take a look inside the code of the module&#039;s files you want to implement:&lt;br /&gt;
* module&#039;s table name: either find it by yourself in the database (looks like llx_themodulename) or by printing it in actions_customfields.class.php by adding &#039;&#039;&#039;print_r($parameters)&#039;&#039;&#039; and search for $parameters-&amp;gt;table_element&lt;br /&gt;
* module&#039;s context: search for &amp;quot;callHooks(&amp;quot; without the quotes or take a look at the wiki: http://wiki.dolibarr.org/index.php/Hooks_system&lt;br /&gt;
or you can get it by printing it in the /htdocs/customfields/class/actions_customfields.class.php by adding print_r($parameters) and search for $parameters-&amp;gt;context&lt;br /&gt;
* module&#039;s trigger actions: search for &amp;quot;run_triggers(&amp;quot; or take a look at the wiki: http://wiki.dolibarr.org/index.php/Triggers#List_of_known_triggers_actions&lt;br /&gt;
* module&#039;s idvar: look at the url when you access one of the module&#039;s record, and you should see something like fiche.php?xxxid=12 - you need the xxxid (eg: socid, facid, etc.).&lt;br /&gt;
* module&#039;s security: in the page shown in URL when accessing the module&#039;s datasheet, edit this file and add &#039;&#039;&#039;print($user-&amp;gt;rights)&#039;&#039;&#039;, then in the list try to find the correct right (generally something like $user-&amp;gt;modulename-&amp;gt;creer). You can also try to search for $user-&amp;gt;rights in the sourcecode of this module.&lt;br /&gt;
&lt;br /&gt;
With these values, edit the config file (/htdocs/customfields/conf/conf_customfields.lib.php), particularly the $modulesarray and $triggersarray variables:&lt;br /&gt;
&lt;br /&gt;
=== Configure hooks and main parameters ===&lt;br /&gt;
&lt;br /&gt;
Add every parameters except triggers in the $modulesarray:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$modulesarray = array(&lt;br /&gt;
                      array(&#039;context&#039;=&amp;gt;&#039;invoicecard&#039;, &#039;table_element&#039;=&amp;gt;&#039;facture&#039;, &#039;idvar&#039;=&amp;gt;&#039;facid&#039;, &#039;rights&#039;=&amp;gt;array(&#039;$user-&amp;gt;rights-&amp;gt;facture-&amp;gt;creer&#039;)), // Client Invoice&lt;br /&gt;
                      array(&#039;context&#039;=&amp;gt;&#039;membercard&#039;, &#039;table_element&#039;=&amp;gt;&#039;adherent&#039;, &#039;idvar&#039;=&amp;gt;&#039;rowid&#039;, &#039;rights&#039;=&amp;gt;array(&#039;$user-&amp;gt;rights-&amp;gt;adherent-&amp;gt;creer&#039;), &#039;tabs&#039;=&amp;gt;array(&#039;objecttype&#039;=&amp;gt;&#039;member_admin&#039;, &#039;function&#039;=&amp;gt;&#039;member_admin_prepare_head&#039;, &#039;lib&#039;=&amp;gt;DOL_DOCUMENT_ROOT.&#039;/core/lib/member.lib.php&#039;)), // Members / Adherents&lt;br /&gt;
                     ); // Edit me to add the support of another module - NOTE: Lowercase only!&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are also a lot of others parameters you can give to CustomFields so that the module gets better managed or to add a few other functionnalities, here is a non-exhaustive list:&lt;br /&gt;
* context: name of the module&#039;s context.&lt;br /&gt;
* table_element: name of the sql table that holds the object&#039;s datas.&lt;br /&gt;
* idvar: name of the variable that holds the id of the object in the sql database (by default &#039;id&#039; or &#039;rowid&#039;).&lt;br /&gt;
* rights: prevent the custom fields to be editable unless all the rights set here are enabled (preventing custom fields to be viewed is useless because they won&#039;t show anyway if the page can&#039;t be loaded due to insufficient rights, so this is directly handled by the module).&lt;br /&gt;
* tabs=&amp;gt;array(objecttype, function, lib, tabname, tabtitle): used to embed CustomFields&#039;s admin panel into another module&#039;s admin page.&lt;br /&gt;
&lt;br /&gt;
=== Configure creation triggers ===&lt;br /&gt;
&lt;br /&gt;
Add the triggers in $triggersarray:&lt;br /&gt;
$triggersarray = array(&amp;quot;order_create&amp;quot;=&amp;gt;&amp;quot;commande&amp;quot;,&lt;br /&gt;
                       &amp;quot;yourmodule_triggeraction1&amp;quot;=&amp;gt;&amp;quot;yourmodule_tablename&amp;quot;,&lt;br /&gt;
                       &amp;quot;yourmodule_triggeraction2&amp;quot;=&amp;gt;&amp;quot;yourmodule_tablename&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
Note: generally you will be looking to implement the _CREATE.&lt;br /&gt;
&lt;br /&gt;
Note2: if a triggeraction is not recognized by CustomFields (eg: mymodule_addpayment) then by default CustomFields will assimilate it to a _create action (it will create and save the values for the custom fields, just for a module&#039;s creation page).&lt;br /&gt;
&lt;br /&gt;
Note3: CustomFields also supports a few other triggers like _CLONE, _UPDATE, _MODIFY, but they are not required and may not work depending on how your module works to manage these actions.&lt;br /&gt;
&lt;br /&gt;
=== Configuration troubleshooting ===&lt;br /&gt;
&lt;br /&gt;
* If you cannot see the custom fields appear on your module&#039;s page, this means the hooks are not implemented or configured in CustomFields, please read the related chapters.&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet but there&#039;s no edit button icon: the module implements security but you did not configure it properly in CustomFields, please find the right security in $user-&amp;gt;rights and set it in conf_customfields.lib.php.&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the creation page but are not saved: you forgot to set the trigger in $triggersarray, or the creation trigger does not exist for this module and you have to implement it. Jump to [[Module_CustomFields_Dev#Triggers]].&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet but not on the creation page (or vice versa): A hook is missing on the page where the custom fields do not appear. Jump to [[Module_CustomFields_Dev#Hooks]].&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet with the edit button, but when the edit button is clicked, everything disappear: either the $idvar is not properly set (look at the URL to know if you need to set idvar), or the hook is not properly made ($object is not defined and it&#039;s called by another name, but the hook use $object instead of the correct variable).&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet with the edit button, can be edited but cannot be saved (the data is not saved): same as the previous point: either $idvar or $object bug.&lt;br /&gt;
&lt;br /&gt;
Also you might find useful to &#039;&#039;&#039;[[Module_CustomFields#General_advices_for_troubleshooting|enable php errors]]&#039;&#039;&#039; to better track errors if that&#039;s not already done.&lt;br /&gt;
&lt;br /&gt;
= Example cases =&lt;br /&gt;
&lt;br /&gt;
You can find a compilation of concrete, practical examples on the [[Module_CustomFields_Cases]] wiki page.&lt;br /&gt;
&lt;br /&gt;
= TO DO =&lt;br /&gt;
&lt;br /&gt;
You will find below the public list of features that should be implemented in the future.&lt;br /&gt;
&lt;br /&gt;
If you have an idea about a new feature that isn&#039;t listed below, feel free to add it to the list!&lt;br /&gt;
&lt;br /&gt;
== To Do ==&lt;br /&gt;
&lt;br /&gt;
* Better management of hidden fields with custom cascading (eg: works great on creation form, but on edit form and creation form with edit action, if the parents already have a value, the hidden fields will stay hidden! This is because of the AJAX is not called, and this is normal). -&amp;gt; Propose a better custom cascade function storing the hidden field&#039;s state inside extraoptions?&lt;br /&gt;
&lt;br /&gt;
== To Document ==&lt;br /&gt;
* Automatic recopy on cloning for custom fields and products&#039; lines custom fields.&lt;br /&gt;
* new API function: customfields_clone_or_recopy($object, $fromobject, $action2 = null)&lt;br /&gt;
&lt;br /&gt;
== Should do ==&lt;br /&gt;
* Add the possibility to mark a custom field to be shown in tables listings (eg: as a column in products listings) with &amp;quot;order by this column&amp;quot; buttons and stuffs. Will be difficult if the hooks are not yet implemented in listings.&lt;br /&gt;
* Checkbox (multiple checkboxes, one for each possible value) and multiselect (big select and can use SHIFT to multiselect) (store in XML file) (but impossible with constrained fields, can&#039;t store multiple foreign keys ids in one single sql field, but would work for other types like enum)&lt;br /&gt;
use fieldset, input checkbox and label for, eg:&lt;br /&gt;
    &amp;lt;fieldset&amp;gt;&amp;lt;legend&amp;gt;&amp;lt;label for=&amp;quot;cf_productslist&amp;quot;&amp;gt; $langs-&amp;gt;trans(&#039;cf_productslist&#039;)&amp;lt;/label&amp;gt;&amp;lt;/legend&amp;gt;&lt;br /&gt;
    &amp;lt;input type=&amp;quot;checkbox&amp;quot; name=&amp;quot;cf_productslist_choice1&amp;quot; value=&amp;quot;1&amp;quot; id=&amp;quot;cf_productslist_choice1&amp;quot; /&amp;gt;&amp;lt;label for=&amp;quot;cf_productslist_choice1&amp;quot;&amp;gt; $langs-&amp;gt;trans(&#039;cf_productslist_choice1&#039;)&amp;lt;/label&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
    &amp;lt;input type=&amp;quot;checkbox&amp;quot; name=&amp;quot;cf_productslist_choice2&amp;quot; value=&amp;quot;1&amp;quot; id=&amp;quot;cf_productslist_choice2&amp;quot; /&amp;gt;&amp;lt;label for=&amp;quot;cf_productslist_choice2&amp;quot;&amp;gt; $langs-&amp;gt;trans(&#039;cf_productslist_choice2&#039;)&amp;lt;/label&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
    etc...&lt;br /&gt;
    &amp;lt;/fieldset&amp;gt;&lt;br /&gt;
* Add a javascript options values generator for the enum type (a hidden input that would be shown only when DropdownBox type is selected and that would permit to add options by clicking a plus button).&lt;br /&gt;
* Replace triggers in customfields class by hookmanager?&lt;br /&gt;
* AJAX previsualisation of the HTML custom field that will be created.&lt;br /&gt;
* In ODTs, create special tags to directly access day, month or year value of a date type field independently of the rest.&lt;br /&gt;
* New overloading function&#039;s action: &amp;quot;get&amp;quot;, similar to view except that it works not only on Dolibarr&#039;s view but whenever someone tries to access this field&#039;s value.&lt;br /&gt;
* Add Upload and Image field types (this would allow for an easier integration inside ODT and PDF templates than the current workflow of Dolibarr).&lt;br /&gt;
&lt;br /&gt;
== Known bugs ==&lt;br /&gt;
* in product and service modules, if you edit a field, the proposals and other fields below won&#039;t be shown, you need to refresh the page. This problem resides in Dolibarr I think (since we are simply using a hook).&lt;br /&gt;
* ficheinter (Intervention module) custom fields will stay in create mode just after create page is submitted (this is because of the $action that is not standard - $action=edit or something).&lt;br /&gt;
&lt;br /&gt;
== Never/Maybe one day ==&lt;br /&gt;
* Add support for repeatable (predefined) invoices (the way it is currently managed makes it very difficult to manage this without making a big exception, adding specific functions in customfields modules that would not at all will be reusable anywhere else, when customfields has been designed to be as generic as possible to support any module and any version of dolibarr, because it&#039;s managed by a totally different table while it&#039;s still managed by the same module, CustomFields work with the paradigm: one module, one table).&lt;br /&gt;
* Add an AJAX select box for constrained values : when a constrained type is selected and a table is selected, a hidden select box would show up with the list of the fields of this table to choose the values that will be printed as the values for this customfield (eg: for table llx_users you could select the &amp;quot;nom&amp;quot; field and then it would automatically prepend &amp;quot;nom_&amp;quot; to the field&#039;s name).&lt;br /&gt;
* Fine-grained rights management: rights per group, per user, and per field.&lt;br /&gt;
* Replace overloading functions (extend class) by hookmanager if possible (but how to simulate the *full switch? Plus this will force users to make their own modules to make those functions, and to disable/renable their modules everytime they will add a new function...).&lt;br /&gt;
* Refactor the trigger array: merge it with the modulesarray (along with a new way to specify which customfields trigger action a trigger should correspond, eg: &#039;linebill_insert&#039;=&amp;gt;&#039;customfields_create&#039;). But the triggers aren&#039;t necessarily associated with a specific module...&lt;br /&gt;
* AJAX autocompletion / auto population of field&#039;s content when typing the beginning of a value in a text box.&lt;/div&gt;</summary>
		<author><name>Lrq3000</name></author>
	</entry>
	<entry>
		<id>https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Dev&amp;diff=33145</id>
		<title>Module CustomFields Dev</title>
		<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Dev&amp;diff=33145"/>
		<updated>2015-02-11T18:20:04Z</updated>

		<summary type="html">&lt;p&gt;Lrq3000: /* Never/Maybe one day  */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:CustomFields]]&lt;br /&gt;
{{TemplateDocDevEn}}&lt;br /&gt;
{{TemplateModEN}}&lt;br /&gt;
{{BasculeDevUserEn|&lt;br /&gt;
name=CustomFields|&lt;br /&gt;
num=8500|&lt;br /&gt;
devdoc=This page|&lt;br /&gt;
userdoc=[[Module_CustomFields]]|}}&lt;br /&gt;
[[Category:FAQ EN]]&lt;br /&gt;
&lt;br /&gt;
These notes are aimed to developpers who want to extend the functionnalities of the module or want to use it for extreme cases.&lt;br /&gt;
This is not meant to guide implementers on how to implement and configure the module (see the related chapter for that).&lt;br /&gt;
&lt;br /&gt;
If you are an implementer and just want to use CustomFields with its normal functions, you can read the [[Module_CustomFields|user documentation of CustomFields]].&lt;br /&gt;
&lt;br /&gt;
You can also jump directly to [[Module_CustomFields_Cases]] wiki page for a collection of practical examples.&lt;br /&gt;
&lt;br /&gt;
= Principles =&lt;br /&gt;
&lt;br /&gt;
CustomFields is simply a wrapper for SQL databases.&lt;br /&gt;
&lt;br /&gt;
The module is based on SQL to manage the fields and most of the stuff. It uses and understands standard SQL.&lt;br /&gt;
&lt;br /&gt;
The main principles of CustomFields are:&lt;br /&gt;
&lt;br /&gt;
* one SQL column = one Dolibarr field.&lt;br /&gt;
* one SQL type = one method to show and edit the field in Dolibarr.&lt;br /&gt;
* almost everything should be configurable from the configuration file, or admin interface.&lt;br /&gt;
* be independent from Dolibarr&#039;s data structures.&lt;br /&gt;
&lt;br /&gt;
CustomFields sits besides Dolibarr, and does not mix with it, even if in the Dolibarr&#039;s interface it looks like CustomFields is embedded inside Dolibarr, in reality, CustomFields is totally independent.&lt;br /&gt;
&lt;br /&gt;
= Goals =&lt;br /&gt;
&lt;br /&gt;
This module was made to provide an easy way to manage custom sql fields, not only for users and implementers, but also for developpers to use in their own module:&lt;br /&gt;
* by easily adding the support of custom fields for your own module by using this module;&lt;br /&gt;
* by reusing custom fields from another module in your own module;&lt;br /&gt;
* by managing sql fields in your own module by using the CustomFields class (which will take care of everything from printing, editing to PDF/ODT templating).&lt;br /&gt;
&lt;br /&gt;
So this module is not only an auxiliary of other modules, but it can also be the core part of your own module by using it as your SQL query class for fields.&lt;br /&gt;
&lt;br /&gt;
= SQL structure =&lt;br /&gt;
&lt;br /&gt;
A CustomFields table needs to respect three parameters that never change:&lt;br /&gt;
&lt;br /&gt;
* table&#039;s name (format: llx_*dolibarrmodule*_customfields where *dolibarrmodule* = table_element in conf_customfields.lib.php)&lt;br /&gt;
* first column: rowid, which is the primary key for each field&lt;br /&gt;
* second column: fk_*dolibarrmodule* which is the foreign key that links every customfields record to the Dolibarr&#039;s module that is associated (invoice, order, etc...).&lt;br /&gt;
&lt;br /&gt;
Apart from the table&#039;s name and the first two special columns, every other columns are just standard sql columns that you can freely create, edit or delete as you want, from the module&#039;s administration page or from a third-party SQL management tool (such as phpMyAdmin).&lt;br /&gt;
&lt;br /&gt;
You can also create more complex sql fields: constraints, foreign keys, transformations, other sql types, etc...).&lt;br /&gt;
&lt;br /&gt;
Then the module will try to manage the sql fields you manually created the best it can:&lt;br /&gt;
* by printing appropriate input fields in the Dolibarr gui interface.&lt;br /&gt;
* by providing the developper an easy access to these fields in php (eg: $customfields-&amp;gt;cf_myfield or $object-&amp;gt;customfields-&amp;gt;cf_myfield) for any use, including PDF templates and ODT templates, or in any php script (so that you can use these fields in your own module).&lt;br /&gt;
&lt;br /&gt;
= SQL Indexes =&lt;br /&gt;
&lt;br /&gt;
The module automatically adds an index on the primary key and foreign key (first and second columns).&lt;br /&gt;
&lt;br /&gt;
To further optimize the performances of your database, and particularly the customfields tables, you may want to manually add indexes based on usage.&lt;br /&gt;
&lt;br /&gt;
Also, you may add indexes on remote tables used by constrained fields, which could optimize even more the performances since constrained fields have the worst performance drawback for CustomFields.&lt;br /&gt;
&lt;br /&gt;
= SQL Compatibility mode =&lt;br /&gt;
&lt;br /&gt;
The module relies on referential integrity checks and foreign keys of your DBMS to keep the database clean and to provide the constrained fields feature. Referential integrity checks is part of the SQL standards and should be used whenever possible, which always was a main goal for CustomFields (to be respectful of SQL standards).&lt;br /&gt;
&lt;br /&gt;
Unluckily, a few DBMS systems still don&#039;t support this critical functionality of the SQL standard, for example MySQL enables MyISAM engine by default, which doesn&#039;t support this feature.&lt;br /&gt;
&lt;br /&gt;
In these cases, you can either do two things:&lt;br /&gt;
* Switch to an engine which support referential integrity. For example with MySQL, you can enable InnoDB, which support referential integrity checks and foreign keys.&lt;br /&gt;
* Use CustomFields compatibility mode (more infos below).&lt;br /&gt;
&lt;br /&gt;
In the case your database does not support referential integrity checks, CustomFields will automatically switch to the SQL compatibility mode, which will allow you to use CustomFields even on databases where foreign keys are not possible.&lt;br /&gt;
&lt;br /&gt;
In compatibility mode, you will get nearly all CustomFields advanced features such as Constrained fields, but the customfields&#039; tables in the database won&#039;t be cleaned automatically (no delete on cascade, meaning that when you delete an object, the linked custom fields will still be kept in your database), and constrained fields won&#039;t be updated automatically on update.&lt;br /&gt;
&lt;br /&gt;
Nevertheless, these missing cleanup functionalities won&#039;t be seen by your users because they will never be accessible: on deletion of an object, the custom fields won&#039;t ever be accessible again from Dolibarr&#039;s interface, and object&#039;s ids are never changed since Dolibarr also relies on this. Thus in compatibility mode, CustomFields will act just like normally, but your database will be a bit less clean.&lt;br /&gt;
&lt;br /&gt;
= JSON extension for extra options =&lt;br /&gt;
&lt;br /&gt;
== Introduction to JSON in CustomFields ==&lt;br /&gt;
The relational model is very great to store and fetch data with a declarative language, and this is very powerful, but this model also imposes a few limitations, for example the relations must be well formatted and predefined.&lt;br /&gt;
&lt;br /&gt;
That&#039;s why we finally implemented a &#039;&#039;&#039;JSON extension&#039;&#039;&#039; to manage &#039;&#039;&#039;extra options&#039;&#039;&#039; and associate them with custom fields. This allows to add many properties (or columns if you are more used to the relational model) on-the-fly without having to predefine them prior to storing and using them.&lt;br /&gt;
&lt;br /&gt;
This allows to add an undefined number of columns/properties/extra options that will help manage special behaviours for custom fields. For example, it is currently used to allow to change the order of appearance of custom fields, and to store and manage remote table and columns datas for constrained fields on DBMS that do not support Foreign Keys checks.&lt;br /&gt;
&lt;br /&gt;
We chose JSON over XML because the library are simpler and the data footprint smaller, and because CustomFields does not need advanced features of XML like XQuery, data typing and meta structures (at least not for now).&lt;br /&gt;
&lt;br /&gt;
The other advantage is that php objects can directly be encoded into JSON without any preprocessing, and can then as straightly be decoded from JSON back into a php object.&lt;br /&gt;
&lt;br /&gt;
A simple mean to encode and decode back an object into and from a storage place, as well as allow users to easily add/remove extra options were the main requirement, and JSON (+ JSON libraries for PHP) meet this criteria.&lt;br /&gt;
&lt;br /&gt;
However, note that the extra options are now encoded as an associative array, so as to avoid any ambiguities (there&#039;s no associative array in JSON, they are converted to objects).&lt;br /&gt;
&lt;br /&gt;
== Implementation of JSON ==&lt;br /&gt;
The implementation is very simple: a table called &#039;&#039;&#039;llx_customfields_extraoptions&#039;&#039;&#039; is created when you enable the CustomFields module, and this table only contains three columns:&lt;br /&gt;
* table_name (varchar)&lt;br /&gt;
* column_name (varchar)&lt;br /&gt;
* extraoptions (blob)&lt;br /&gt;
&lt;br /&gt;
(table_name, column_name) both forms the primary key of the table, and links to columns (custom fields) of other customfields tables.&lt;br /&gt;
extraoptions is a blob, which allows to store the data as binary (as-is) without encoding (which solves a big problem of JSON which is encoding special and foreign characters, here there is no problem at all).&lt;br /&gt;
&lt;br /&gt;
When fetching custom fields, the module simply JOIN the extraoptions table, which adds an &#039;&#039;&#039;extraoptions&#039;&#039;&#039; column for each custom field in the SQL result. Then it is quite easy to just json_decode() the data returned inside the extraoptions column.&lt;br /&gt;
&lt;br /&gt;
This methodology allows to have all the advantages of a relational database, with the flexibility of a free-schema model:&lt;br /&gt;
* everything is stored inside the database (only one storage place)&lt;br /&gt;
* fixed columns allows for relational joining and optimization&lt;br /&gt;
* thus queries (fetching) are optimised&lt;br /&gt;
* at the same time, the extraoptions are automatically retrieved and associated with every custom field, and whenever needed they can be used&lt;br /&gt;
* and thus we also get a flexible model to &#039;&#039;&#039;add extra (virtual) columns whenever needed&#039;&#039;&#039; without having to alter the relational database schema&lt;br /&gt;
&lt;br /&gt;
Thus no critical information is and should be stored inside the extraoptions column because they would not be query-able, only extra options that are just used to tweak the management of those fields inside the PHP application should be stored. But this is a wonderful way to add non critical informations that still allows to develop more complex features and management for special custom fields.&lt;br /&gt;
&lt;br /&gt;
== CustomFields JSON API ==&lt;br /&gt;
&lt;br /&gt;
=== Storing extra options ===&lt;br /&gt;
&lt;br /&gt;
You can use the &#039;&#039;&#039;setExtra()&#039;&#039;&#039; method to set extra options of a custom field.&lt;br /&gt;
&lt;br /&gt;
Eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$extra = array(); // $extra should always be an associative array, it&#039;s no longer an object&lt;br /&gt;
$extra[&#039;myproperty&#039;] = &#039;Anything I want here&#039;; // set a property/extra option like this&lt;br /&gt;
$extra[&#039;category&#039;][&#039;subcategory&#039;][&#039;prop&#039;] = true; // we can set recursive properties, there&#039;s not any limit to how deep the hierarchy can be&lt;br /&gt;
&lt;br /&gt;
require_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomField($db, &#039;currentmodule&#039;);&lt;br /&gt;
$fieldid = &#039;mycustomfields&#039;; // $fieldid can either be a column_name or an ordinal_position from information_schema.columns&lt;br /&gt;
$result = $customfields-&amp;gt;setExtra($fieldid, $extra); // store the extra options inside the database&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can also use addCustomField() or updateCustomField() with an $extra array, and these functions will also call the setExtra() method.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: the extra options you set are &#039;&#039;&#039;appended&#039;&#039;&#039; to the ones already stored in the database. However, you can also overwrite previous options, just use the same name of property but with a different value (the replacement is recursive, you can do it at any level in the hierarchy).&lt;br /&gt;
&lt;br /&gt;
=== Fetching extra options ===&lt;br /&gt;
&lt;br /&gt;
Extra options are automatically fetched along with a custom field structure, decoded, and stored inside the extra sub-property of any field object, or CustomFields object.&lt;br /&gt;
&lt;br /&gt;
When fetched, extra options are stored as a sub-object for the current field object (which itself is stored as a subobject of the CustomFields object if caching is enabled).&lt;br /&gt;
&lt;br /&gt;
Eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
require_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomField($db, &#039;currentmodule&#039;);&lt;br /&gt;
$fields = fetchAllFieldsStruct(); // fetching fields structure will automatically fetch extra options at the same time. This works with fetchAllFieldsStruct() and fetchFieldStruct()&lt;br /&gt;
&lt;br /&gt;
// Now that you fetched the fields structure, you can just access the extra options sub-properties:&lt;br /&gt;
print_r($fields-&amp;gt;mycustomfield-&amp;gt;extra); // will print all extra options for the field mycustomfield&lt;br /&gt;
print_r($customfields-&amp;gt;fields-&amp;gt;mycustomfield-&amp;gt;extra); // you can also access the same field(s) directly from the CustomFields object if caching is enabled (default behaviour)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Modifying extra options ===&lt;br /&gt;
&lt;br /&gt;
You can just combine the two previous points to edit extra options of a custom field: just load a custom field, edit on-the-fly the extra property with PHP code, and then store it back directly into the database:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
require_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomField($db, &#039;currentmodule&#039;);&lt;br /&gt;
$fields = fetchAllFieldsStruct();&lt;br /&gt;
&lt;br /&gt;
$fields-&amp;gt;mycustomfield-&amp;gt;extra[&#039;newproperty&#039;] = &#039;This is a new property&#039;; // add a new property over the old extra options sub-object&lt;br /&gt;
$fields-&amp;gt;mycustomfield-&amp;gt;extra[&#039;mycategory&#039;] = &#039;Overwrite the old category&#039;; // this will replace the previous value of &#039;mycategory&#039; extra option&lt;br /&gt;
&lt;br /&gt;
$customfields-&amp;gt;setExtra(&#039;mycustomfield&#039;, $fields-&amp;gt;mycustomfield-&amp;gt;extra); // store the modified extra options back into the database for future usage&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= CustomFields API =&lt;br /&gt;
&lt;br /&gt;
== Introduction to CustomFields API ==&lt;br /&gt;
&lt;br /&gt;
This chapter will cover the basics on how to manually and concretely use the CustomFields class to create/update/fetch/manage custom fields.&lt;br /&gt;
&lt;br /&gt;
This will give you a better understanding of how CustomFields works, as well as giving you insights on how to use it for your own applications.&lt;br /&gt;
&lt;br /&gt;
Important note: contrary to the Facade API, the core CustomFields class use columns names without any prefix, so don&#039;t try to use the prefix, it won&#039;t work! For example: if your field is named &#039;mycustomfield&#039; and prefix is &#039;cf_&#039;, when you use the Facade API you should use &#039;$object-&amp;gt;$path-&amp;gt;cf_mycustomfield&#039; but when you use the core CustomFields class directly, $fieldname = &#039;mycustomfield&#039;.&lt;br /&gt;
&lt;br /&gt;
== Prerequisite to use the CustomFields class ==&lt;br /&gt;
&lt;br /&gt;
Before using the CustomFields class, you first must instanciate it. This is a necessary preliminary step before using any of the methods that will be covered inside this chapter, and thus will not be repeated, only shown once here.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Include the CustomFields class (which contains the main API)&lt;br /&gt;
require_once(dirname(__FILE__).&#039;/../class/customfields.class.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Instanciate a CustomFields object&lt;br /&gt;
global $db; // the database handler created by Dolibarr&lt;br /&gt;
$table_element = &#039;currentmodule&#039;; // where $table_element is the database&#039;s table name for the module, without the Dolibarr prefix (eg: for &#039;llx_facture&#039;, table_element is &#039;facture&#039;). You can also find it inside the $object properties (just print_r() it).&lt;br /&gt;
$customfields = new CustomFields($db, $table_element); // instanciate the CustomFields class inside $customfields object&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
CustomFields keeps track of errors that can happen when executing methods and SQL queries inside the &#039;&#039;&#039;error&#039;&#039;&#039; and &#039;&#039;&#039;errors&#039;&#039;&#039; properties, which you can print with the printErrors() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Do something with CustomFields before&lt;br /&gt;
&lt;br /&gt;
if (count($customfields-&amp;gt;errors) &amp;gt; 0) { // If there is at least one error&lt;br /&gt;
    $customfields-&amp;gt;printErrors(); // print errors&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// You can also print your own error message, in two manners:&lt;br /&gt;
&lt;br /&gt;
// 1- submit your own message&lt;br /&gt;
$customfields-&amp;gt;printErrors(&#039;Your own error message here&#039;); // this will print ONLY your error message, using the Dolibarr&#039;s AJAX error dialog (standard way of printing errors)&lt;br /&gt;
&lt;br /&gt;
// 2- append your message in the customfields errors&lt;br /&gt;
$customfields-&amp;gt;errors[] = &#039;Your own error message to be appended with other CustomFields messages&#039;;&lt;br /&gt;
$customfields-&amp;gt;printErrors(); // Here your error message will be appended, thus all error messages will be printed. This allows you for example to add more intelligible informations on a specific error.&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== General purpose SQL methods ==&lt;br /&gt;
&lt;br /&gt;
CustomFields provide an interface to easily execute various common SQL requests, as well as your own custom SQL requests.&lt;br /&gt;
&lt;br /&gt;
* fetchAllTables() fetch a list of all the tables in the database&lt;br /&gt;
* fetchPrimaryField($table) fetch the column_name (and only the column_name!) of the primary field of a table. You can then use fetchFieldStruct()  with the column_name to get any information you want.&lt;br /&gt;
* fetchAny($columns, $table, $where=&#039;&#039;, $orderby=&#039;&#039;, $limitby=&#039;&#039;) allows you to issue any simple SQL query command, for example fetchAny(&#039;*&#039;, &#039;sometable&#039;) is equivalent to SELECT * FROM sometable; (but then everything is managed automatically and result is returned to you)&lt;br /&gt;
* executeSQL($sql, $eventname) to execute any SQL query you want, this will return you a resource (and NOT a processed nuplet like fetchAny!). $eventname can be anything, it&#039;s just for Dolibarr logging facility.&lt;br /&gt;
* executeMultiSQL($sql, $eventname) to execute multiple SQL queries&lt;br /&gt;
* fetchFieldStruct($id=null, $nohide=false, $table=null, $cachepath=null) allows you to fetch the field&#039;s SQL structure of not only custom fields, but of any database&#039;s column using the $table variable. Eg: fetchFieldStruct(&#039;mycolumn&#039;, true, &#039;mytable&#039;);&lt;br /&gt;
* probeTable($table) helps you to check if a table exists in the database&lt;br /&gt;
* fetch_object($resource) will return a PHP object from a $resource, similarly to mysql_fetch_object() but with two advantages: it works on several DBMS (not only mysql), and it always return lowercase properties names (else depending on the configuration of the DBMS, the request can return uppercase or mixed case properties names).&lt;br /&gt;
* escape() and reverse_escape() are similare to mysql_real_escape but is reversible and DBMS-independent (so you can use it with other DBMSes).&lt;br /&gt;
&lt;br /&gt;
== Managing CustomFields structure ==&lt;br /&gt;
&lt;br /&gt;
The first step into CustomFields is to create and manage the structure of a custom field.&lt;br /&gt;
&lt;br /&gt;
=== Creating a custom field definition ===&lt;br /&gt;
&lt;br /&gt;
To create a custom field, you can use the addCustomField() method.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;addCustomField($fieldname, $type, $size, $nulloption, $defaultvalue = null, $constraint = null, $customtype = null, $customdef = null, $customsql = null, $fieldid = null, $extra = null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will take care of forging the (complicated) SQL query with the forgeSQLCustomField() method, and will return you an error if there was a problem.&lt;br /&gt;
&lt;br /&gt;
=== Updating a custom field definition ===&lt;br /&gt;
&lt;br /&gt;
To update a custom field, you can use the updateCustomField() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;&lt;br /&gt;
updateCustomField($fieldid, $fieldname, $type, $size, $nulloption, $defaultvalue, $constraint = null, $customtype = null, $customdef = null, $customsql = null, $extra = null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: the updateCustomField() and addCustomField() methods can both be used since updateCustomField() is in fact an alias for addCustomField(). CustomFields will automatically detect if a custom field already exists, and update it if that&#039;s true, or create it.&lt;br /&gt;
&lt;br /&gt;
Note2: updating a custom field will automatically take care of the constraints and foreign keys on its own, thus you can change the foreign key, or remove it altogether if set to null.&lt;br /&gt;
&lt;br /&gt;
=== Delete a custom field definition ===&lt;br /&gt;
&lt;br /&gt;
You can easily delete a custom field by column_name or by ordinal_position:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;deleteCustomField($id);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: constraints and foreign keys will also be deleted automatically so you shouldn&#039;t encounter any error.&lt;br /&gt;
&lt;br /&gt;
=== Fetch one custom field&#039;s SQL structure ===&lt;br /&gt;
&lt;br /&gt;
To get the SQL structure of a custom field, which can be quite useful to know for example its datatype or if it has a foreign key, or accepts null values, you can use the fetchFieldStruct() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
fields = $customfields-&amp;gt;fetchFieldStruct($id=null, $nohide=false, $table=null, $cachepath=null); // where $id can be a column_name, an ordinal_position from information_schema.columns or null if you want to fetch all the columns at once&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method will either return null, either a single field object, either an array on field objects.&lt;br /&gt;
&lt;br /&gt;
Note: this function will use caching by default to accelerate future results, but you can disable it, or even change the default cache path. For more informations please read the comments in the source code.&lt;br /&gt;
&lt;br /&gt;
=== Fetch several custom fields&#039; SQL structures ===&lt;br /&gt;
&lt;br /&gt;
Use the fetchAllFieldsStruct() method, which is an alias for fetchFieldStruct():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;fetchAllFieldsStruct($nohide=false, $table=null, $cachepath=null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: this method will always return an array of fields objects, even if only one is found (for more consistency), and this is the main difference with fetchFieldStruct(null).&lt;br /&gt;
&lt;br /&gt;
== Managing custom fields records ==&lt;br /&gt;
&lt;br /&gt;
Now that you know how to create and manage the structure of custom fields, we can proceed on to managing the records.&lt;br /&gt;
&lt;br /&gt;
=== Creating a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Simply use the create() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;create($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
$object must be an object containing as its properties the customfields data.&lt;br /&gt;
&lt;br /&gt;
For example, if you have a field named mycustomfield, your object must have $object-&amp;gt;mycustomfields defined.&lt;br /&gt;
&lt;br /&gt;
This method will store in the database ALL the custom fields that were created/edited and with a valid value (eg: an empty value for a field that does not accept null value is invalid) AT ONCE! So you can modify all the columns of one record at once, but not several records at once.&lt;br /&gt;
&lt;br /&gt;
Concretely, this is very useful with Dolibarr, because modules tends to store everything inside a single object generally called $object, and CustomFields tries to hook onto $object, so that you can just pass $object as is to create() and it will save all the custom fields.&lt;br /&gt;
&lt;br /&gt;
But you can also create an empty object with $object = new stdClass(); and then manually fill the custom fields records with whatever you want, and then call the create() method, this will work as well!&lt;br /&gt;
&lt;br /&gt;
You can also just modify only one column of one record, by just specifying one custom field column_name in your $object. The other custom fields that are not specified won&#039;t be created/modified.&lt;br /&gt;
&lt;br /&gt;
=== Updating a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Use the update() method, which is an alias for the create() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;update($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As noted above, update() is an alias of the create() method, which means that CustomFields will try to detect by itself if a record needs to be updated or created.&lt;br /&gt;
&lt;br /&gt;
=== Deleting a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Use the delete() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;delete($id); // where $id is = to fk_moduleid column, NOT rowid&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fetching a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Just use the fetch() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$records = $customfields-&amp;gt;fetch($id=null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where $id can either be = fk_moduleid (NOT rowid), or null to fetch all the records at once.&lt;br /&gt;
&lt;br /&gt;
This method will either return null, a single record object, or an array of records objects.&lt;br /&gt;
&lt;br /&gt;
=== Fetching all custom fields&#039; records ===&lt;br /&gt;
&lt;br /&gt;
Use the fetchAll() method, which is an alias for fetch():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$records = $customfields-&amp;gt;fetchAll();&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: this method will always return an array of records object, for more consistency (and this is the main difference with fetch(null) ).&lt;br /&gt;
&lt;br /&gt;
=== Fetching referenced records of a constrained field ===&lt;br /&gt;
&lt;br /&gt;
A constrained custom field points to another database table, and a value for this constrained field is the rowid of a record in the referenced table.&lt;br /&gt;
&lt;br /&gt;
Thus, the principal purpose of a constrained custom field is not the value of the field in itself (it&#039;s simply an integer), but rather the other fields available from the referenced table, the constrained field being in fact just a pointer.&lt;br /&gt;
&lt;br /&gt;
To fetch the remote record from the referenced table, use the fetchReferencedValuesList() of the CustomFields class:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$fkrecord = fetchReferencedValuesList($field, $id=null, $where=null, $allcolumns=false);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
&lt;br /&gt;
* $field is a custom field object (fetched using fetchFieldStruct() or fetchAllFieldsStruct()),&lt;br /&gt;
* $id is the rowid of the record to get, just like fetch() and fetchAll(). Can be null to fetch all remote records (or to use the $where clause).&lt;br /&gt;
* $where is a string without the WHERE clause (eg: &amp;quot;fk_facture=1&amp;quot;) to get finer control. In this case, you can use $id or set $id=null depending on your purpose. This is mainly a private argument to allow for cascade option.&lt;br /&gt;
* $allcolumns if false, it will fetch only the required columns to do the smart value substitution. If true, it will fetch all remote columns (useful for ODT substitution). If an array of strings, each string will be a column to fetch from the remote database.&lt;br /&gt;
&lt;br /&gt;
Note that this function is NOT recursive (it will fetch the records from the referenced table, and that&#039;s all, it won&#039;t fetch the custom fields of the referenced table nor recursively fetch the remote constrained custom fields). To do recursive fetching, see in the Facade API.&lt;br /&gt;
&lt;br /&gt;
Note2: this function automatically manages Smart Value Substitution depending of the $field-&amp;gt;column_name.&lt;br /&gt;
&lt;br /&gt;
=== Recursive fetching referenced records of a constrained field ===&lt;br /&gt;
&lt;br /&gt;
If you want to recursively fetch a constrained field (eg: constrainedfield1-&amp;gt;ref_table-&amp;gt;constrainedfield2-&amp;gt;ref_table2-&amp;gt;...), you need another function called fetchReferencedValuesRec(). Technically, this function is in the Facade API (inside /customfields/lib/customfields_aux.lib.php), but it isn&#039;t meant to be used by lambda users (contrary to customfields_fill_object()), but is mainly a helper function for the rest of the facade API.&lt;br /&gt;
&lt;br /&gt;
fetchReferencedValuesRec() works very similarly to fetchReferencedValuesList(), but it will work recursively.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$fkrecord = fetchReferencedValuesRec($customfields, $field, $id, $recursive=true)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where $customfields is the CustomFields instance for the module you are working on, $field is the constrained field you want to fetch, $id is the record&#039;s id you want to fetch, and $recursive enables or disables the recursion.&lt;br /&gt;
&lt;br /&gt;
Note that this function automatically manages Smart Value Substitution (and also recursively).&lt;br /&gt;
&lt;br /&gt;
Note2: This function tries to automatically avoid an infinite recursion loop by using a $blacklist: for a given constrained custom field, a table won&#039;t be visited twice. This means that for example you can do: invoice-&amp;gt;user-&amp;gt;socpeople-&amp;gt;invoice, this will work and every referenced fields will be fetched, but it will automatically stop the recursion at the second invoice, because else it could continue in a loop to user, socpeople, invoice, user, socpeople, etc.&lt;br /&gt;
&lt;br /&gt;
Note3: when adding recursively referenced fields, the key in the returned array will be prefixed by the name of the custom field that led to those remote fields. Thus, you can have something like &amp;quot;cf_myfield1_cf_remotefield1_cf_remotefield2&amp;quot; if you have a constrained field cf_myfield1-&amp;gt;cf_remotefield1-&amp;gt;cf_remotefield2.&lt;br /&gt;
&lt;br /&gt;
== Input forms for custom fields ==&lt;br /&gt;
&lt;br /&gt;
The API provides some neat ways to automatically manage the input forms for users to input new values, with the best adapted HTML form based on the custom field&#039;s data type.&lt;br /&gt;
&lt;br /&gt;
The following methods will print input fields that are best adapted to the SQL data type of your custom fields as well as figure out the possible values and even format them (for example: a not null enum will be printed as a HTML select list without an empty option; fetch automatically remote columns for constrained fields and show them in a select).&lt;br /&gt;
&lt;br /&gt;
So you don&#039;t have to bother how your data will be presented to the user nor how it will be processed.&lt;br /&gt;
&lt;br /&gt;
=== Printing an input form adapted to the field&#039;s data type ===&lt;br /&gt;
&lt;br /&gt;
showInputField() is a sort of converter customfieldstructure -&amp;gt; HTML input field.&lt;br /&gt;
&lt;br /&gt;
Use the &#039;&#039;&#039;showInputField()&#039;&#039;&#039; method to print the right input form for a custom field.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;showInputField($field, $currentvalue=null, $moreparam=&#039;&#039;, $ajax_php_callback=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
showInputField() takes 4 parameters:&lt;br /&gt;
&lt;br /&gt;
* $field is a CustomFields structure object (eg: returned by fetchFieldStruct()) and is necessary to detect the right datatype and the possible values.&lt;br /&gt;
* $currentvalue is the current value if you want to preselect a value (for example if the user already defined a value, you can reload it here). This is optional, in case it is not defined, either a null/empty value will be chosen, or either the default value defined when creating the custom field.&lt;br /&gt;
* $moreparam is used to add more HTML attributes, if you want for example to add some css styling or whatever you want.&lt;br /&gt;
* $ajax_php_callback is optional and allows to specify the relative url to the php callback script. If specified, an AJAX script will be automatically attached to this field (see AJAX-PHP callback below).&lt;br /&gt;
&lt;br /&gt;
showInputField() only prints ONE HTML input field, thus you have to enclose it/them by your own HTML form.&lt;br /&gt;
&lt;br /&gt;
eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print &#039;&amp;lt;form method=&amp;quot;get&amp;quot; action=&amp;quot;?&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
print &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;customtoken&amp;quot; value=&amp;quot;ABCDEFGH&amp;quot; /&amp;gt;&#039;; // example of a custom hidden HTML input field, that can be used to pass a token for example&lt;br /&gt;
// Loop over each custom fields (you have to load them before with $customfields-&amp;gt;fetchAllFieldsStruct()&lt;br /&gt;
foreach ($customfields-&amp;gt;fields as $field) {&lt;br /&gt;
    print $customfields-&amp;gt;showInputField($field, $currentvalue=null, $moreparam=&#039;&#039;);&lt;br /&gt;
}&lt;br /&gt;
print &#039;&amp;lt;/form&amp;gt;&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can also store the resulting HTML input field and process it by a regexp for example:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$myhtmlfield = $customfields-&amp;gt;showInputField($field, $currentvalue=null, $moreparam=&#039;&#039;);&lt;br /&gt;
preg_replace($myhtmlfield, ..., ...);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: for constrained fields, if the name follows the convention of &#039;&#039;&#039;Smart Value Substitution&#039;&#039;&#039; (ie: remote columns names are included in the custom field&#039;s name), showInputField() will &#039;&#039;&#039;automatically fetch the remote values, format them, and present them&#039;&#039;&#039; in a HTML select.&lt;br /&gt;
&lt;br /&gt;
=== Printing an input form for a custom field ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;showInputForm()&#039;&#039;&#039; is a function used to print a single (and only one!) custom field with an adapted HTML input field, but enclosed by a HTML form. This is used for example when you want to allow to edit a single custom field but not the others.&lt;br /&gt;
&lt;br /&gt;
showInputForm() is in fact a wrapper for showInputField(), thus it will call showInputField() and simply enclose it by a form.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;showInputForm($id, $field, $currentvalue=null, $idvar=&#039;id&#039;, $page=null, $moreparam=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== AJAX-PHP callback ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;showInputFieldAjax()&#039;&#039;&#039; will output an AJAX script (using jQuery) to send and receive data to and from a specified php script. The AJAX script will automatically manage HTML form input&#039;s updating as long as the received data follows some convention.&lt;br /&gt;
&lt;br /&gt;
An AJAX script can be attached to any field (including non-CustomFields) by using the method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;showInputFieldAjax($id, $php_callback_url, $on_func=&amp;quot;change&amp;quot;, $request_type=&amp;quot;post&amp;quot;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
* $id is the HTML id of the field you want to attach the AJAX to.&lt;br /&gt;
* $phpcallback the relative path from Dolibarr&#039;s htdocs root folder to the php script that will receive and send back data.&lt;br /&gt;
* $on_func is the Javascript or jQuery event that should trigger the AJAX script (by default on change).&lt;br /&gt;
* $request_type is the HTML query type that will be used to send the data to the PHP callback (either &amp;quot;get&amp;quot; or &amp;quot;post&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
The AJAX script will automatically manage sending/receiving of data:&lt;br /&gt;
&lt;br /&gt;
* automatically send all form&#039;s inputs values, as well as the current calling field&#039;s name and value. The sending happens on change by default, but another event can be set using the $on_event argument. Data will be sent using standard GET or POST.&lt;br /&gt;
* Upon reception of data from the php script, the AJAX script will automatically parse the data and update the HTML fields.&lt;br /&gt;
&lt;br /&gt;
The PHP callback script must send back to AJAX the data in JSON encoded format, and with with a specific format:&lt;br /&gt;
&lt;br /&gt;
* the data must be an associative array, where each entry&#039;s key is the name (not ID!) of an HTML field to update.&lt;br /&gt;
* each entry&#039;s value is an associative array, where each sub-entry&#039;s key is the action to do. Available actions: &#039;options&#039;, &#039;value&#039;, &#039;html&#039;, &#039;alert&#039;.&lt;br /&gt;
* each sub-entry&#039;s value is the value for this action and HTML field.&lt;br /&gt;
&lt;br /&gt;
Thus, this approach allows to generically support a wide range of updating actions, managed automatically.&lt;br /&gt;
&lt;br /&gt;
You, the implementer, have just to edit the PHP callback script and return an associative array defining what action you want for each field you want to update. This is exactly what has been implemented in the Custom AJAX Functions library, which simplifies even more your job by managing automatically the step described here (generating AJAX and formatting data), so that you can focus on &#039;&#039;&#039;making the data&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Printing custom fields in documents ==&lt;br /&gt;
&lt;br /&gt;
The API can also automatically manage the printing and formatting of custom fields in your ODT, PDF and HTML documents.&lt;br /&gt;
&lt;br /&gt;
=== Printing in HTML ===&lt;br /&gt;
&lt;br /&gt;
You can either just print the value as-is, or pass them through the printField() or simpleprintField() function to format them (very useful for constrained fields) - see below.&lt;br /&gt;
&lt;br /&gt;
=== Printing in documents ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;printField()&#039;&#039;&#039; is used to properly print and translate a custom field&#039;s record&#039;s value. Generally, you can just print the custom field&#039;s value as-is, but in a few cases (eg for boolean types, or constrained fields), using the printField() method will more properly print the value.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;printField($field, $value, $outputlangs=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
* $field is the custom field&#039;s SQL structure as an object (eg: returned by fetchFieldStruct()).&lt;br /&gt;
* $value is the custom field&#039;s value for the current record (eg: $customfields-&amp;gt;fields-&amp;gt;myfield).&lt;br /&gt;
* $outputlangs is the Translate object for the target language.&lt;br /&gt;
&lt;br /&gt;
For example, with a constrained field with Smart Value Substitution, instead of printing the id like 1, it will print the full name of user like FirstName Name.&lt;br /&gt;
&lt;br /&gt;
This method will also translate a value if a translation is found in the language files.&lt;br /&gt;
&lt;br /&gt;
Another method &#039;&#039;&#039;printFieldPDF()&#039;&#039;&#039; can be used to properly print a value for a PDF (properly encoding the characters).&lt;br /&gt;
&lt;br /&gt;
Powertip: this function can also be used to get the Smart Value Substituted value of the field (instead of printing the rowid) in the field is constrained.&lt;br /&gt;
&lt;br /&gt;
=== Simple printing in documents ===&lt;br /&gt;
&lt;br /&gt;
If you don&#039;t want to have to manually fetch the structure of every custom field whose you want to print the value, you can then use a simplifier method called &#039;&#039;&#039;simpleprintField()&#039;&#039;&#039; which will take as input a custom field name instead of a custom field SQL structure object.&lt;br /&gt;
&lt;br /&gt;
This method is just a simplifier (Facade) wrapper for the printField() method, and thus will have the same advantages (fetch constrained fields, values translation, etc.).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;simpleprintField($fieldname, $value, $outputlangs=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
* $fieldname is the custom field&#039;s name (eg: &amp;quot;myfield&amp;quot;).&lt;br /&gt;
* $value is the custom field&#039;s value for the current record (eg: $customfields-&amp;gt;fields-&amp;gt;myfield).&lt;br /&gt;
* $outputlangs is the Translate object for the target language.&lt;br /&gt;
&lt;br /&gt;
Another method &#039;&#039;&#039;simpleprintFieldPDF()&#039;&#039;&#039; can be used to properly print a value for a PDF (properly encoding the characters).&lt;br /&gt;
&lt;br /&gt;
Powertip: this function can also be used to get the Smart Value Substituted value of the field (instead of printing the rowid) in the field is constrained.&lt;br /&gt;
&lt;br /&gt;
=== Find the label ===&lt;br /&gt;
&lt;br /&gt;
Finding the label of a custom field might not be as simple as it seems. First, values of properties objects may be easily used, but their name (key) is a worst job. Secondly, custom fields&#039; labels can be translated, and the following functions are expressly made for that purpose. Thirdly, for PDF and some other documents you have to encode the characters if you want them to properly be drawn.&lt;br /&gt;
&lt;br /&gt;
So if you want to print a custom field&#039;s label with translation (multilingual) support, just use the &#039;&#039;&#039;findLabel()&#039;&#039;&#039; method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$translatedfieldname = $customfields-&amp;gt;findLabel($fieldname, $outputlangs = &#039;&#039;); // where $fieldname is just a custom field label (eg: cf_mycustomfield or mycustomfield, both will work) and $outputlangs is the target language object (see the Translate class native of Dolibarr)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As for the other printing methods, you also have the &#039;&#039;&#039;findLabelPDF()&#039;&#039;&#039; wrapper method to easily print labels that with characters encoded for PDF.&lt;br /&gt;
&lt;br /&gt;
= Facade API =&lt;br /&gt;
&lt;br /&gt;
The Facade API was made to ease the usage of the CustomFields class with the Dolibarr system. It provides a neat way to do many operations with single commands, and will automatically manage the loading of the CustomFields class, and its usage with many optimizations.&lt;br /&gt;
&lt;br /&gt;
== customfields_fill_object() ==&lt;br /&gt;
&lt;br /&gt;
You can load the Facade API and then use the &#039;&#039;&#039;customfields_fill_object()&#039;&#039;&#039; to automatically fetch the fields structures and the records, and also automatically link to remote fields when a constrained field is found. This is an all-in-one solution to easily fetch CustomFields datas, and is also optimized to fully use caching and other special properties of the CustomFields class.&lt;br /&gt;
&lt;br /&gt;
Concretely, you just have to include the Facade API, then pass the customfields_fill_object() with a Dolibarr $object, and the Facade API will return a CustomFields object with appended the custom fields records and also the custom fields&#039; SQL structures.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$customfields = customfields_fill_object($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* $object will then include the custom fields records associated with this object (eg: $object-&amp;gt;customfields-&amp;gt;cf_yourfield).&lt;br /&gt;
* $customfields will be a CustomFields object containing the fields structures (eg: $customfields-&amp;gt;fields-&amp;gt;yourfield).&lt;br /&gt;
&lt;br /&gt;
You can also call any CustomFields function if you need to using the $customfields object, eg: $customfields-&amp;gt;simpleprintField($object-&amp;gt;customfields-&amp;gt;cf_yourfield);&lt;br /&gt;
&lt;br /&gt;
Note: $object can also be a dummy object containing only two properties that are required: $object-&amp;gt;id and $object-&amp;gt;table_element.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will now describe the full specification of this function:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$customfields = customfields_fill_object(&amp;amp;$object,$fromobject = null, $outputlangs = null, $prefix = null,$pdfformat = false);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* $object is the object where the customfields will be saved&lt;br /&gt;
* $fromobject is the object from where the customfields will be fetched (this can be a different module than $object, or can be null to be the same as $object)&lt;br /&gt;
* $outputlangs is the target language for translation of fields value (if empty, the default current global translation object will be used)&lt;br /&gt;
* $prefix allows you to save the custom fields in a sub-object. By default, when $prefix=null, custom fields are saved in the path $object-&amp;gt;customfields, but when you specify a prefix, custom fields will be saved inside $object-&amp;gt;customfields-&amp;gt;$prefix. This may be useful if you want to store multiple object&#039;s custom fields or records in the same $object.&lt;br /&gt;
* $pdfformat defines if you want a special formatting for the values: if null, there&#039;s no formatting, if false, there is a simple formatting (eg: translation of fields&#039; values), if true then the formatting will be adapted for PDF templating.&lt;br /&gt;
&lt;br /&gt;
For more informations on pratical usages of this function, please [[Module_CustomFields#Implementing_in_php_code_.28dolibarr_core_modules_or_your_own_module.29|read the user documentation on how to implement CustomFields in php code]] or [[Module_CustomFields_Cases#Linking_Dolibarr_objects_from_two_different_modules|read an example case of linking two objects of different modules together]].&lt;br /&gt;
&lt;br /&gt;
== customfields_fill_object_lines() ==&lt;br /&gt;
&lt;br /&gt;
Similar to customfields_fill_object() for normal custom fields, there is also a &#039;&#039;&#039;customfields_fill_object_lines()&#039;&#039;&#039; to fetch custom fields associated with lines of products/services. This function is also optimized and it is strongly advised to use it and to not try to fetch manually this kind of custom fields (ie: associated with lines of products/services rather than objects), because there are a lot of optimizations going on behind the scene and that are very hard to do manually.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields for lines of products/services (you can then access them by doing $object-&amp;gt;customfields-&amp;gt;lines-&amp;gt;$lineid-&amp;gt;cf_yourfield)&lt;br /&gt;
$customfields = customfields_fill_object_lines($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Requirements: $object can be a dummy object but must contain at least 2 properties: $object-&amp;gt;id and $object-&amp;gt;table_element_line.&lt;br /&gt;
&lt;br /&gt;
For more informations on pratical usages of this function, please [[Module_CustomFields#Implementing_in_php_code_.28dolibarr_core_modules_or_your_own_module.29|read the user documentation on how to implement CustomFields in php code]].&lt;br /&gt;
&lt;br /&gt;
= Architecture =&lt;br /&gt;
Here is a full list of the CustomFields packaged files with a short description (for a more in-depth view just crawl the source files, they are full of comments):&lt;br /&gt;
&lt;br /&gt;
== Core files ==&lt;br /&gt;
files that are necessary for the CustomFields to work, they contains the core functions&lt;br /&gt;
&lt;br /&gt;
* /customfields/admin/customfields.php --- Administrator&#039;s configuration panel : this is where you create and manage the custom fields definitions&lt;br /&gt;
* /customfields/class/actions_customfields.class.php --- Hooks class : used to hook into Dolibarr core modules without altering any core file (can be used to hook into your own modules too). Also used to preprocess the parameters before calling the printforms library.&lt;br /&gt;
* /customfields/class/customfields.class.php --- Core class : every database action is made here in this class. You can find some printing functions because they are very generic.&lt;br /&gt;
* /customfields/conf/conf_customfields.lib.php --- Configuration file : contains the main configurable variables to adapt CustomFields to your needs or to expand its support to other modules and more native sql types.&lt;br /&gt;
* /customfields/conf/conf_customfields_func.lib.php --- Auxiliary functions library to read and manage config variables&lt;br /&gt;
* /customfields/core/modules/modCustomFields.class.php --- Dolibarr&#039;s module definition file : this is a core file necessary for Dolibarr to recognize the module and to declare the hooks to Dolibarr (but it does not store anything else than meta-informations).&lt;br /&gt;
* /customfields/core/substitutions/functions_customfields.lib.php --- CustomFields substitution class for ODT generation : necessary to support customfields tags in ODT files&lt;br /&gt;
* /customfields/core/triggers/interface_50_modCustomFields_SaveFields.class.php --- Core triggers file : this is where the actions on records are managed. This is an interface between other modules and CustomFields management. You should not modify this file but you should add your triggers in $triggersarray inside conf_customfields.lib.php, but if you really have a special need and know what you do, you can edit this file to directly add your own triggers. Also, if CustomFields are shown in the forms but cannot be modified nor saved, then probably this trigger file is not detected by Dolibarr (maybe you can try to lower the number 50 to raise the priority?).&lt;br /&gt;
* /customfields/fields/customfields_fields_extend.lib.php.example --- Overloading functions library with users functions: this is where implementers can use their own php code to manage the custom fields they want (one function per action and per field).&lt;br /&gt;
* /customfields/fields/customfields_fields_ajax_custom.lib.php.example --- Custom AJAX functions library with users functions: this is where implementers can use their own php code to manage the custom fields cascading and AJAX calls, similarly to the Overloading functions library (one function per action and per field).&lt;br /&gt;
* /customfields/langs/code_CODE/customfields.lang --- Core language file : this is where you can translate the admin config panel (data types names, labels, descriptions, etc.)&lt;br /&gt;
* /customfields/langs/code_CODE/customfields-user.lang --- User defined language file : this is where you can store the labels and values of your custom fields (see the related chapter)&lt;br /&gt;
* /customfields/lib/customfields_aux.lib.php --- Simplifier library to easily use CustomFields class to populate an $object with custom fields datas (Facade design pattern). This is what you should use for most of your needs.&lt;br /&gt;
* /customfields/lib/customfields_printforms.lib.php --- Core printing library for records : contains only printing functions, there&#039;s not really any core functions inside but it allows to manage the printing of the custom fields records and their editing.&lt;br /&gt;
* /customfields/lib/customfields_ajax_wrapper.lib.php --- AJAX wrapper library to automatically manage cascading at objects creation. This isn&#039;t really necessary for CustomFields to function, this is more an addition to allow dynamical AJAX updating, but CustomFields core functionalities can work without this library.&lt;br /&gt;
* /customfields/sql/*.sql --- Unused (the tables are created directly via a function in the customfields.class.php) - but the sql files are still valid, they were used as squeletton in the first designs.&lt;br /&gt;
&lt;br /&gt;
== PDFTest module ==&lt;br /&gt;
optional module to append a page listing all custom fields values on newly generated PDF documents. To be used to test if custom fields are working correctly before making one&#039;s own template.&lt;br /&gt;
&lt;br /&gt;
* /customfieldspdftest/class/actions_customfieldspdftest.class.php --- Main functions library and hooks class, to hook onto pdf generation. Contains the PDFTest functions.&lt;br /&gt;
* /customfieldspdftest/core/modules/modCustomFieldsPDFTest.class.php --- Module descriptor for Dolibarr&lt;br /&gt;
&lt;br /&gt;
== Runtime outline ==&lt;br /&gt;
&lt;br /&gt;
Here is an outline of how CustomFields works: what functions will be called and why:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At admin page:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* No external function call (apart for some icons or needed admin functions like for tabs), everything that is done are sql queries via CustomFields class (to fetch and modify the sql columns in the customfields tables).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At printing:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Module&#039;s hook formObjectOptions calls Dolibarr&#039;s hookmanager&lt;br /&gt;
* Hookmanager calls CustomFields /class/actions_customfields.class.php, function formObjectOptions() of CustomFields&lt;br /&gt;
* Preprocess a few parameters (mainly the $action) and calls customfields_print_forms()&lt;br /&gt;
* customfields_print_forms() preprocess all the hook&#039;s parameters and assimilate them with the CustomFields&#039;s configuration parameters (there&#039;s no connection to the database at this stage, only parameters preprocessing), then calls /lib/customfields_printforms.lib.php, functions customfields_print_creation_form() or customfields_print_datasheet_form&lt;br /&gt;
* both customfields_print_creation_form() lookup the database using CustomFields class and prints empty custom fields; customfields_print_datasheet_form() lookup the database using CustomFields class, fetch the values of the custom fields and prints them;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At edition:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Same steps as &#039;&#039;&#039;At printing&#039;&#039;&#039;&lt;br /&gt;
* then customfields_print_datasheet_form() process the custom field being edited differently than the others: if editing, it prints the html input field (to enter a new value), or if saving it just saves the data into the database by calling the CUSTOMFIELDS_MODIFY trigger and then continue to print the other fields normally.&lt;br /&gt;
* CUSTOMFIELDS_MODIFY trigger calls $customfields-&amp;gt;update($newrecord);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At creation (saving):&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Module&#039;s creation trigger MYMODULE_CREATE calls Dolibarr&#039;s run_trigger()&lt;br /&gt;
* Dolibarr run_trigger() calls CustomFields trigger file /core/trigger/interface_50_modCustomFields_SaveFields.class.php, function run_trigger()&lt;br /&gt;
* CustomFields&#039;s run_trigger() assimilate the given parameters with the CustomFields&#039;s config parameters, and if the trigger is found in $triggersarray, then the appropriate action is taken (by default: _CREATE or another standard action if recognized), if not in $triggersarray, the function will still try to automatically detect the appropriate action by detecting MYMODULE_ALISTOFACTIONS with a regular expression.&lt;br /&gt;
* The appropriate action is then done (creation in this case) by calling $customfields-&amp;gt;create($record).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At edition or creation of product lines:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Similar to &#039;&#039;&#039;At edition&#039;&#039;&#039; but with hooks: formCreateProductOptions and formEditProductOptions.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At ODT document generation and substitution:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Module&#039;s ODT generator calls for Dolibarr&#039;s function complete_substitution_array(), which then calls for CustomFields substitution file /core/substitution/functions_customfields.lib.php, function customfields_completesubstitutionarray()&lt;br /&gt;
* CustomFields&#039;s function customfields_completesubstitutionarray() calls the simplifier library customfields_aux.lib.php, function customfields_fill_object() and make the substitution inside the $substitutionarray.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At ODT document generation and substitution for product lines:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Same as the previous point but this function is called for every product lines, and thus the data is cached. Also the simplifier function is here called customfields_fill_object_lines(), and the CustomFields&#039;s substitution function called customfields_completesubstitutionarray_lines().&lt;br /&gt;
&lt;br /&gt;
= How to add the support of a new module =&lt;br /&gt;
&lt;br /&gt;
There are two cases: either the module already implements hooks, and you just have to configure CustomFields to support it, or either the module does not implements hooks nor triggers.&lt;br /&gt;
&lt;br /&gt;
This chapter will guide you through the steps to make any module supported by CustomFields (be it a Dolibarr core module or your own module).&lt;br /&gt;
&lt;br /&gt;
== Preliminary test ==&lt;br /&gt;
&lt;br /&gt;
This preliminary test will allow you to know at a glance how much work needs to be done in order to make your module supported by CustomFields, and it will also give you all the config parameters if they are available.&lt;br /&gt;
&lt;br /&gt;
Important note: this test will only give data about hooks, which is the main part to make CustomFields work for your module. But for triggers, there is no way to test, so you will have to check the triggers manually (either by testing if the custom fields are saved at creation page, or by looking at the source code of the objects classes).&lt;br /&gt;
&lt;br /&gt;
Edit the php script of the module (check the URL when you access the module&#039;s datasheet or creation page - attention both are two different pages, so you have to do this twice), then put this at the very &#039;&#039;&#039;end&#039;&#039;&#039; of the php script:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// CustomFields debuglines to show config parameters for current module&lt;br /&gt;
print(&amp;quot;&amp;lt;pre&amp;gt;&amp;quot;); // html nice output&lt;br /&gt;
print(&amp;quot;table_element: {$object-&amp;gt;table_element} - table_element_line: {$object-&amp;gt;table_element_line} - contexts: &amp;quot;);&lt;br /&gt;
print_r($hookmanager-&amp;gt;contextarray);&lt;br /&gt;
print(&amp;quot; - conf hooks_modules: &amp;quot;);&lt;br /&gt;
print_r($conf-&amp;gt;hooks_modules);&lt;br /&gt;
print(&amp;quot; - hooks attached: &amp;quot;);&lt;br /&gt;
print_r($hookmanager-&amp;gt;hooks);&lt;br /&gt;
print(&amp;quot; - user rights: &amp;quot;);&lt;br /&gt;
print_r($user-&amp;gt;rights);&lt;br /&gt;
print(&amp;quot;&amp;lt;/pre&amp;gt;&amp;quot;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now reload the page, and scroll at the very bottom. You should see some informations about the module.&lt;br /&gt;
&lt;br /&gt;
Depending on the data available, you will know what you have to do:&lt;br /&gt;
&lt;br /&gt;
* If all the datas is available, then it&#039;s ok, jump to [[Module_CustomFields_Dev#Configuring_CustomFields_to_support_the_module]] and copy these datas to the parameters of conf_customfields.lib.php&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;table_element&#039;&#039;&#039;: $object is not defined, try to find how is called the main object variable and replace $object by this variable in the debug code given above (eg: in Agenda, the object variable is called $agenda and $agendacomm).&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;table_element_line&#039;&#039;&#039;: either there&#039;s no products lines for this module, either the main object variable is not called $object (see the previous point). One last case is that the object is not standard and does not store table_element_line (the name of the table that stores its products lines), in which case you have to manually add it in the class (eg: $this-&amp;gt;table_element_line = &#039;something&#039;).&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;contexts&#039;&#039;&#039;: the hookmanager is not defined, and probably there&#039;s no hook at all in this module, so you&#039;ll have to implement them by yourself. Jump to [[Module_CustomFields_Dev#Hooks]].&lt;br /&gt;
&lt;br /&gt;
* If there is a list of &#039;&#039;&#039;contexts&#039;&#039;&#039; but the module cannot be found in the list: the hookmanager is defined but the module has not defined a context, you still have to implement the hooks. Jump to [[Module_CustomFields_Dev#Hooks]].&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;conf hooks_modules&#039;&#039;&#039;: this variable is not necessary, empty or not it doesn&#039;t matter. But if you see customfields and/or customfieldspdftest inside the list, it is a confirmation that CustomFields indeed is hooking the module correctly.&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;hooks attached&#039;&#039;&#039;, or CustomFields cannot be found in the list: CustomFields has not been properly configured to hook to this module, or you forgot to disable then re enable CustomFields in order to update the CustomFields&#039;s hooks. Jump to [[Module_CustomFields_Dev#Configuring_CustomFields_to_support_the_module]].&lt;br /&gt;
&lt;br /&gt;
* If there is no &#039;&#039;&#039;$user-&amp;gt;rights&#039;&#039;&#039;: there is either no standard security implemented in this module, or either the variable is called with another name. If there is no security, you don&#039;t need to configure it in CustomFields, but if there is a security, you need to find how the variable is called (like $object can get another name sometimes).&lt;br /&gt;
&lt;br /&gt;
* If there is &#039;&#039;&#039;$user-&amp;gt;rights&#039;&#039;&#039; but the module can&#039;t be found in the list: either the name for the rights is different (this often happens, the rights array is not yet standardized), or either the rights are hardcoded into the module&#039;s code (rights should be announced to the $user-&amp;gt;rights array, but sometimes it happens that the dev forgot to do so, and thus the right is entirely hardcoded and you can&#039;t find it in the dynamic rights array). In any of these two cases, you will have to manually read the module&#039;s code to find the right. Also, maybe there is no security implemented, but since $user-&amp;gt;rights is declared this is less probable.&lt;br /&gt;
&lt;br /&gt;
Here is an example of output on a correctly hooked module (invoice):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
table_element: facture - table_element_line: facturedet - contexts: Array&lt;br /&gt;
(&lt;br /&gt;
    [0] =&amp;gt; searchform&lt;br /&gt;
    [1] =&amp;gt; leftblock&lt;br /&gt;
    [2] =&amp;gt; toprightmenu&lt;br /&gt;
    [3] =&amp;gt; invoicecard&lt;br /&gt;
)&lt;br /&gt;
 - conf hooks_modules: Array&lt;br /&gt;
(&lt;br /&gt;
    [customfields] =&amp;gt; invoicecard:propalcard:productcard:ordercard:thirdpartycard:contactcard:ordersuppliercard:invoicesuppliercard:membercard:actioncard:projectcard:projecttaskcard:contractcard:interventioncard:doncard:tripsandexpensescard:taxvatcard:expeditioncard:invoicecard:propalcard:ordercard:ordersuppliercard:invoicesuppliercard&lt;br /&gt;
    [customfieldspdftest] =&amp;gt; Array&lt;br /&gt;
        (&lt;br /&gt;
            [0] =&amp;gt; pdfgeneration&lt;br /&gt;
            [1] =&amp;gt; ordersuppliercard&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
)&lt;br /&gt;
 - hooks attached: Array&lt;br /&gt;
(&lt;br /&gt;
    [invoicecard] =&amp;gt; Array&lt;br /&gt;
        (&lt;br /&gt;
            [customfields] =&amp;gt; ActionsCustomFields Object&lt;br /&gt;
                (&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
)&lt;br /&gt;
 - user rights: stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [user] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [user] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [lire] =&amp;gt; 1&lt;br /&gt;
                    [creer] =&amp;gt; 1&lt;br /&gt;
                    [password] =&amp;gt; 1&lt;br /&gt;
                    [supprimer] =&amp;gt; 1&lt;br /&gt;
                    [export] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [self] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [creer] =&amp;gt; 1&lt;br /&gt;
                    [password] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [user_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [readperms] =&amp;gt; 1&lt;br /&gt;
                    [write] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [self_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [readperms] =&amp;gt; 1&lt;br /&gt;
                    [writeperms] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [group_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [read] =&amp;gt; 1&lt;br /&gt;
                    [readperms] =&amp;gt; 1&lt;br /&gt;
                    [write] =&amp;gt; 1&lt;br /&gt;
                    [delete] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    [facture] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [lire] =&amp;gt; 1&lt;br /&gt;
            [creer] =&amp;gt; 1&lt;br /&gt;
            [invoice_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [unvalidate] =&amp;gt; 1&lt;br /&gt;
                    [send] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [valider] =&amp;gt; 1&lt;br /&gt;
            [paiement] =&amp;gt; 1&lt;br /&gt;
            [supprimer] =&amp;gt; 1&lt;br /&gt;
            [facture] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [export] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    [propale] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [lire] =&amp;gt; 1&lt;br /&gt;
            [creer] =&amp;gt; 1&lt;br /&gt;
            [valider] =&amp;gt; 1&lt;br /&gt;
            [propal_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [send] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [cloturer] =&amp;gt; 1&lt;br /&gt;
            [supprimer] =&amp;gt; 1&lt;br /&gt;
            [export] =&amp;gt; 1&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    [produit] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [lire] =&amp;gt; 1&lt;br /&gt;
            [creer] =&amp;gt; 1&lt;br /&gt;
            [supprimer] =&amp;gt; 1&lt;br /&gt;
            [export] =&amp;gt; 1&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    etc... a lot of other rights...&lt;br /&gt;
&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When you are finished with implementing the support of CustomFields in the module, you should delete these debug lines.&lt;br /&gt;
&lt;br /&gt;
== Implementing the support of CustomFields in the module ==&lt;br /&gt;
&lt;br /&gt;
If there is a module you want to implement the support of CustomFields, but does not yet has the required components, here is a guide on how to implement these components.&lt;br /&gt;
&lt;br /&gt;
=== Hooks ===&lt;br /&gt;
&lt;br /&gt;
Implementing hooks will allow custom fields to appear on your module&#039;s page. They will also be editable and they will save the data. For creation, you need to use a trigger (see the subchapter below).&lt;br /&gt;
&lt;br /&gt;
First you should be familiar with the way that hooks are implemented in modules. If you don&#039;t, please first [[Hooks_system#Implementation|read the hooks documentation]].&lt;br /&gt;
&lt;br /&gt;
An example case of implementation of hooks into a Dolibarr core file can be found in the [[Module_CustomFields_Cases|CustomFields Example Cases wiki page]].&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Datasheet hook&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
To implement CustomFields in your module, you must initialize the hook manager, then use the following hook context:&lt;br /&gt;
* formObjectOptions&lt;br /&gt;
&lt;br /&gt;
This hook must be placed everywhere in your code where you want custom fields to appear (eg: on the creation page and datasheet view page).&lt;br /&gt;
&lt;br /&gt;
Note: Generally, you will have to place the hook just before the end of a table tag (&#039;&#039;&#039;&amp;lt;/table&amp;gt;&#039;&#039;&#039;), so if you don&#039;t know where to place it (eg: if you&#039;re implementing CustomFields on a Dolibarr core module), just search for this tag in the page shown in the url (eg: /htdocs/commande/fiche.php).&lt;br /&gt;
&lt;br /&gt;
Eg: First load the hookmanager at the beginning of your php script:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Initialize technical object to manage hooks of thirdparties. Note that conf-&amp;gt;hooks_modules contains array array&lt;br /&gt;
include_once(DOL_DOCUMENT_ROOT.&#039;/core/class/hookmanager.class.php&#039;);&lt;br /&gt;
$hookmanager=new HookManager($db);&lt;br /&gt;
$hookmanager-&amp;gt;initHooks(array(&#039;yourmodule&#039;));&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then place the hook as many times as required:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// ... a lot of php code ...&lt;br /&gt;
&lt;br /&gt;
print &#039;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&#039;; // end of the last row of a table&lt;br /&gt;
&lt;br /&gt;
// Other attributes&lt;br /&gt;
$parameters=array(&#039;colspan&#039; =&amp;gt; &#039; colspan=&amp;quot;3&amp;quot;&#039;); // specifying the colspan is optional but will help CustomFields know how to correctly print the table&lt;br /&gt;
$reshook=$hookmanager-&amp;gt;executeHooks(&#039;formObjectOptions&#039;,$parameters,$object,$action);    // Note that $action and $object may have been modified by hook&lt;br /&gt;
&lt;br /&gt;
print &#039;&amp;lt;/table&amp;gt;&#039;; // end of the table, this is what you&#039;re looking for, place the hook just above&lt;br /&gt;
&lt;br /&gt;
// ... a lot of php code ... redo the same if you find another &amp;lt;/table&amp;gt; and there&#039;s a page where there&#039;s no custom fields but you want to place custom fields on there&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note2: If after implementing the hook you receive an error or custom fields don&#039;t appear, you may have to change $object to another variable, depending on what is called the main object (eg: in Agenda it&#039;s called $action and $actioncomm).&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Lines hooks&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
For product lines, you should anyway use the product lines template files: htdocs/core/tpl/freeproductline_edit.tpl.php&lt;br /&gt;
&lt;br /&gt;
If you don&#039;t, you can still manually implement the required hooks, what is required is that you supply $line (which is the current line object being processed) in the $parameters array:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$parameters=array(&#039;line&#039;=&amp;gt;$line,&#039;fk_parent_line&#039;=&amp;gt;$line-&amp;gt;fk_parent_line);&lt;br /&gt;
&lt;br /&gt;
echo $hookmanager-&amp;gt;executeHooks(&#039;formEditProductOptions&#039;,$parameters,$this,$action);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hooks names:&lt;br /&gt;
* formEditProductOptions&lt;br /&gt;
* formCreateProductOptions ($line is not required here, because we create a new product line! you can leave $parameters=array(); empty).&lt;br /&gt;
Note: there&#039;s no hook for viewing, because showing the customfields will clutter the visual field, but if you really want you can also do it by adding a formViewProductOptions hook.&lt;br /&gt;
&lt;br /&gt;
=== Triggers ===&lt;br /&gt;
&lt;br /&gt;
First you need to know how to implement triggers in Dolibarr. For more informations about how to implement triggers in your module, please read the [[Triggers#Manage_and_create_a_new_trigger.27s_action|triggers wiki page]].&lt;br /&gt;
&lt;br /&gt;
An example case of implementation of hooks into a Dolibarr core file can be found in the [[Module_CustomFields_Cases|CustomFields Example Cases wiki page]].&lt;br /&gt;
&lt;br /&gt;
Implementing triggers on your module will allow CustomFields to save the fields at creation (datasheet edition is already managed by hooks).&lt;br /&gt;
&lt;br /&gt;
You need to implement 3 triggers:&lt;br /&gt;
- on creation of object - eg: YOURMODULE_CREATE&lt;br /&gt;
- on creation of lines - eg: YOURMODULELINE_INSERT (optional, only if your module has product lines)&lt;br /&gt;
- on update of lines - eg: YOURMODULELINE_UPDATE (optional, only if your module has product lines)&lt;br /&gt;
&lt;br /&gt;
Note: you need to supply the sql id of the newly created object before calling the trigger.&lt;br /&gt;
&lt;br /&gt;
Eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$this-&amp;gt;id = $this-&amp;gt;db-&amp;gt;last_insert_id(MAIN_DB_PREFIX.&amp;quot;yourmodule_table_element&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
// Appel des triggers&lt;br /&gt;
include_once(DOL_DOCUMENT_ROOT . &amp;quot;/core/class/interfaces.class.php&amp;quot;);&lt;br /&gt;
$interface=new Interfaces($this-&amp;gt;db);&lt;br /&gt;
$result=$interface-&amp;gt;run_triggers(&#039;YOURMODULE_CREATE&#039;,$this,$user,$langs,$conf);&lt;br /&gt;
if ($result &amp;lt; 0) {&lt;br /&gt;
    $error++; $this-&amp;gt;errors=$interface-&amp;gt;errors;&lt;br /&gt;
}&lt;br /&gt;
// Fin appel triggers&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note2: if it&#039;s a Dolibarr core module, you can easily find the right place to put the trigger by searching for &#039;create sql&#039; in the class subfolder (eg: URL is /htdocs/commande/fiche.php, the class is in /htdocs/commande/class/commande.class.php).&lt;br /&gt;
&lt;br /&gt;
Note3: CustomFields also supports a few other triggers like _CLONE, _UPDATE, _MODIFY, but they are not required and may not work depending on how your module works to manage these actions.&lt;br /&gt;
&lt;br /&gt;
== Configuring CustomFields to support the module ==&lt;br /&gt;
&lt;br /&gt;
=== Preliminary work ===&lt;br /&gt;
&lt;br /&gt;
You need to get the parameters of this module, and you will then put them in the CustomFields configuration file (conf_customfields.lib.php).&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Getting the parameters automatically&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
You can use the [[Module_CustomFields_Dev#Preliminary_test|preliminary test]] to easily get all those parameters.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Getting the parameters manually&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
To configure CustomFields to support a module, you need at least 5 parameters:&lt;br /&gt;
* the module&#039;s table_name (name of the sql table where the module stores its datas)&lt;br /&gt;
* the module&#039;s context (name given by the module to the hookmanager)&lt;br /&gt;
* the module&#039;s idvar (name of the variable that holds the sql row id of the record)&lt;br /&gt;
* the module&#039;s trigger action(s) (triggers on creation and update, see the related chapter above)&lt;br /&gt;
* the module&#039;s rights (for security purposes)&lt;br /&gt;
&lt;br /&gt;
To get these parameters manually, you can take a look inside the code of the module&#039;s files you want to implement:&lt;br /&gt;
* module&#039;s table name: either find it by yourself in the database (looks like llx_themodulename) or by printing it in actions_customfields.class.php by adding &#039;&#039;&#039;print_r($parameters)&#039;&#039;&#039; and search for $parameters-&amp;gt;table_element&lt;br /&gt;
* module&#039;s context: search for &amp;quot;callHooks(&amp;quot; without the quotes or take a look at the wiki: http://wiki.dolibarr.org/index.php/Hooks_system&lt;br /&gt;
or you can get it by printing it in the /htdocs/customfields/class/actions_customfields.class.php by adding print_r($parameters) and search for $parameters-&amp;gt;context&lt;br /&gt;
* module&#039;s trigger actions: search for &amp;quot;run_triggers(&amp;quot; or take a look at the wiki: http://wiki.dolibarr.org/index.php/Triggers#List_of_known_triggers_actions&lt;br /&gt;
* module&#039;s idvar: look at the url when you access one of the module&#039;s record, and you should see something like fiche.php?xxxid=12 - you need the xxxid (eg: socid, facid, etc.).&lt;br /&gt;
* module&#039;s security: in the page shown in URL when accessing the module&#039;s datasheet, edit this file and add &#039;&#039;&#039;print($user-&amp;gt;rights)&#039;&#039;&#039;, then in the list try to find the correct right (generally something like $user-&amp;gt;modulename-&amp;gt;creer). You can also try to search for $user-&amp;gt;rights in the sourcecode of this module.&lt;br /&gt;
&lt;br /&gt;
With these values, edit the config file (/htdocs/customfields/conf/conf_customfields.lib.php), particularly the $modulesarray and $triggersarray variables:&lt;br /&gt;
&lt;br /&gt;
=== Configure hooks and main parameters ===&lt;br /&gt;
&lt;br /&gt;
Add every parameters except triggers in the $modulesarray:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$modulesarray = array(&lt;br /&gt;
                      array(&#039;context&#039;=&amp;gt;&#039;invoicecard&#039;, &#039;table_element&#039;=&amp;gt;&#039;facture&#039;, &#039;idvar&#039;=&amp;gt;&#039;facid&#039;, &#039;rights&#039;=&amp;gt;array(&#039;$user-&amp;gt;rights-&amp;gt;facture-&amp;gt;creer&#039;)), // Client Invoice&lt;br /&gt;
                      array(&#039;context&#039;=&amp;gt;&#039;membercard&#039;, &#039;table_element&#039;=&amp;gt;&#039;adherent&#039;, &#039;idvar&#039;=&amp;gt;&#039;rowid&#039;, &#039;rights&#039;=&amp;gt;array(&#039;$user-&amp;gt;rights-&amp;gt;adherent-&amp;gt;creer&#039;), &#039;tabs&#039;=&amp;gt;array(&#039;objecttype&#039;=&amp;gt;&#039;member_admin&#039;, &#039;function&#039;=&amp;gt;&#039;member_admin_prepare_head&#039;, &#039;lib&#039;=&amp;gt;DOL_DOCUMENT_ROOT.&#039;/core/lib/member.lib.php&#039;)), // Members / Adherents&lt;br /&gt;
                     ); // Edit me to add the support of another module - NOTE: Lowercase only!&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are also a lot of others parameters you can give to CustomFields so that the module gets better managed or to add a few other functionnalities, here is a non-exhaustive list:&lt;br /&gt;
* context: name of the module&#039;s context.&lt;br /&gt;
* table_element: name of the sql table that holds the object&#039;s datas.&lt;br /&gt;
* idvar: name of the variable that holds the id of the object in the sql database (by default &#039;id&#039; or &#039;rowid&#039;).&lt;br /&gt;
* rights: prevent the custom fields to be editable unless all the rights set here are enabled (preventing custom fields to be viewed is useless because they won&#039;t show anyway if the page can&#039;t be loaded due to insufficient rights, so this is directly handled by the module).&lt;br /&gt;
* tabs=&amp;gt;array(objecttype, function, lib, tabname, tabtitle): used to embed CustomFields&#039;s admin panel into another module&#039;s admin page.&lt;br /&gt;
&lt;br /&gt;
=== Configure creation triggers ===&lt;br /&gt;
&lt;br /&gt;
Add the triggers in $triggersarray:&lt;br /&gt;
$triggersarray = array(&amp;quot;order_create&amp;quot;=&amp;gt;&amp;quot;commande&amp;quot;,&lt;br /&gt;
                       &amp;quot;yourmodule_triggeraction1&amp;quot;=&amp;gt;&amp;quot;yourmodule_tablename&amp;quot;,&lt;br /&gt;
                       &amp;quot;yourmodule_triggeraction2&amp;quot;=&amp;gt;&amp;quot;yourmodule_tablename&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
Note: generally you will be looking to implement the _CREATE.&lt;br /&gt;
&lt;br /&gt;
Note2: if a triggeraction is not recognized by CustomFields (eg: mymodule_addpayment) then by default CustomFields will assimilate it to a _create action (it will create and save the values for the custom fields, just for a module&#039;s creation page).&lt;br /&gt;
&lt;br /&gt;
Note3: CustomFields also supports a few other triggers like _CLONE, _UPDATE, _MODIFY, but they are not required and may not work depending on how your module works to manage these actions.&lt;br /&gt;
&lt;br /&gt;
=== Configuration troubleshooting ===&lt;br /&gt;
&lt;br /&gt;
* If you cannot see the custom fields appear on your module&#039;s page, this means the hooks are not implemented or configured in CustomFields, please read the related chapters.&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet but there&#039;s no edit button icon: the module implements security but you did not configure it properly in CustomFields, please find the right security in $user-&amp;gt;rights and set it in conf_customfields.lib.php.&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the creation page but are not saved: you forgot to set the trigger in $triggersarray, or the creation trigger does not exist for this module and you have to implement it. Jump to [[Module_CustomFields_Dev#Triggers]].&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet but not on the creation page (or vice versa): A hook is missing on the page where the custom fields do not appear. Jump to [[Module_CustomFields_Dev#Hooks]].&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet with the edit button, but when the edit button is clicked, everything disappear: either the $idvar is not properly set (look at the URL to know if you need to set idvar), or the hook is not properly made ($object is not defined and it&#039;s called by another name, but the hook use $object instead of the correct variable).&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet with the edit button, can be edited but cannot be saved (the data is not saved): same as the previous point: either $idvar or $object bug.&lt;br /&gt;
&lt;br /&gt;
Also you might find useful to &#039;&#039;&#039;[[Module_CustomFields#General_advices_for_troubleshooting|enable php errors]]&#039;&#039;&#039; to better track errors if that&#039;s not already done.&lt;br /&gt;
&lt;br /&gt;
= Example cases =&lt;br /&gt;
&lt;br /&gt;
You can find a compilation of concrete, practical examples on the [[Module_CustomFields_Cases]] wiki page.&lt;br /&gt;
&lt;br /&gt;
= TO DO =&lt;br /&gt;
&lt;br /&gt;
You will find below the public list of features that should be implemented in the future.&lt;br /&gt;
&lt;br /&gt;
If you have an idea about a new feature that isn&#039;t listed below, feel free to add it to the list!&lt;br /&gt;
&lt;br /&gt;
== To Do ==&lt;br /&gt;
&lt;br /&gt;
* Better management of hidden fields with custom cascading (eg: works great on creation form, but on edit form and creation form with edit action, if the parents already have a value, the hidden fields will stay hidden! This is because of the AJAX is not called, and this is normal). -&amp;gt; Propose a better custom cascade function storing the hidden field&#039;s state inside extraoptions?&lt;br /&gt;
&lt;br /&gt;
== To Document ==&lt;br /&gt;
* Automatic recopy on cloning for custom fields and products&#039; lines custom fields.&lt;br /&gt;
* new API function: customfields_clone_or_recopy($object, $fromobject, $action2 = null)&lt;br /&gt;
&lt;br /&gt;
== Should do ==&lt;br /&gt;
* Add the possibility to mark a custom field to be shown in tables listings (eg: as a column in products listings) with &amp;quot;order by this column&amp;quot; buttons and stuffs. Will be difficult if the hooks are not yet implemented in listings.&lt;br /&gt;
* Checkbox (multiple checkboxes, one for each possible value) and multiselect (big select and can use SHIFT to multiselect) (store in XML file) (but impossible with constrained fields, can&#039;t store multiple foreign keys ids in one single sql field, but would work for other types like enum)&lt;br /&gt;
use fieldset, input checkbox and label for, eg:&lt;br /&gt;
    &amp;lt;fieldset&amp;gt;&amp;lt;legend&amp;gt;&amp;lt;label for=&amp;quot;cf_productslist&amp;quot;&amp;gt; $langs-&amp;gt;trans(&#039;cf_productslist&#039;)&amp;lt;/label&amp;gt;&amp;lt;/legend&amp;gt;&lt;br /&gt;
    &amp;lt;input type=&amp;quot;checkbox&amp;quot; name=&amp;quot;cf_productslist_choice1&amp;quot; value=&amp;quot;1&amp;quot; id=&amp;quot;cf_productslist_choice1&amp;quot; /&amp;gt;&amp;lt;label for=&amp;quot;cf_productslist_choice1&amp;quot;&amp;gt; $langs-&amp;gt;trans(&#039;cf_productslist_choice1&#039;)&amp;lt;/label&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
    &amp;lt;input type=&amp;quot;checkbox&amp;quot; name=&amp;quot;cf_productslist_choice2&amp;quot; value=&amp;quot;1&amp;quot; id=&amp;quot;cf_productslist_choice2&amp;quot; /&amp;gt;&amp;lt;label for=&amp;quot;cf_productslist_choice2&amp;quot;&amp;gt; $langs-&amp;gt;trans(&#039;cf_productslist_choice2&#039;)&amp;lt;/label&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
    etc...&lt;br /&gt;
    &amp;lt;/fieldset&amp;gt;&lt;br /&gt;
* Add a javascript options values generator for the enum type (a hidden input that would be shown only when DropdownBox type is selected and that would permit to add options by clicking a plus button).&lt;br /&gt;
* Replace triggers in customfields class by hookmanager?&lt;br /&gt;
* AJAX previsualisation of the HTML custom field that will be created.&lt;br /&gt;
* In ODTs, create special tags to directly access day, month or year value of a date type field independently of the rest.&lt;br /&gt;
* New overloading function&#039;s action: &amp;quot;get&amp;quot;, similar to view except that it works not only on Dolibarr&#039;s view but whenever someone tries to access this field&#039;s value.&lt;br /&gt;
* Add Upload and Image field types (this would allow for an easier integration inside ODT and PDF templates than the current workflow of Dolibarr).&lt;br /&gt;
&lt;br /&gt;
== Known bugs ==&lt;br /&gt;
* in product and service modules, if you edit a field, the proposals and other fields below won&#039;t be shown, you need to refresh the page. This problem resides in Dolibarr I think (since we are simply using a hook).&lt;br /&gt;
* ficheinter (Intervention module) custom fields will stay in create mode just after create page is submitted (this is because of the $action that is not standard - $action=edit or something).&lt;br /&gt;
&lt;br /&gt;
== Never/Maybe one day ==&lt;br /&gt;
* Add support for repeatable (predefined) invoices (the way it is currently managed makes it very difficult to manage this without making a big exception, adding specific functions in customfields modules that would not at all will be reusable anywhere else, when customfields has been designed to be as generic as possible to support any module and any version of dolibarr, because it&#039;s managed by a totally different table while it&#039;s still managed by the same module, CustomFields work with the paradigm: one module, one table).&lt;br /&gt;
* Add an AJAX select box for constrained values : when a constrained type is selected and a table is selected, a hidden select box would show up with the list of the fields of this table to choose the values that will be printed as the values for this customfield (eg: for table llx_users you could select the &amp;quot;nom&amp;quot; field and then it would automatically prepend &amp;quot;nom_&amp;quot; to the field&#039;s name).&lt;br /&gt;
* Fine-grained rights management: rights per group, per user, and per field.&lt;br /&gt;
* Replace overloading functions (extend class) by hookmanager if possible (but how to simulate the *full switch? Plus this will force users to make their own modules to make those functions, and to disable/renable their modules everytime they will add a new function...).&lt;br /&gt;
* Refactor the trigger array: merge it with the modulesarray (along with a new way to specify which customfields trigger action a trigger should correspond, eg: &#039;linebill_insert&#039;=&amp;gt;&#039;customfields_create&#039;). But the triggers aren&#039;t necessarily associated with a specific module...&lt;br /&gt;
* Compatibility with the native Import module.&lt;/div&gt;</summary>
		<author><name>Lrq3000</name></author>
	</entry>
	<entry>
		<id>https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Dev&amp;diff=33144</id>
		<title>Module CustomFields Dev</title>
		<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Dev&amp;diff=33144"/>
		<updated>2015-02-11T18:19:29Z</updated>

		<summary type="html">&lt;p&gt;Lrq3000: /* To Do */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:CustomFields]]&lt;br /&gt;
{{TemplateDocDevEn}}&lt;br /&gt;
{{TemplateModEN}}&lt;br /&gt;
{{BasculeDevUserEn|&lt;br /&gt;
name=CustomFields|&lt;br /&gt;
num=8500|&lt;br /&gt;
devdoc=This page|&lt;br /&gt;
userdoc=[[Module_CustomFields]]|}}&lt;br /&gt;
[[Category:FAQ EN]]&lt;br /&gt;
&lt;br /&gt;
These notes are aimed to developpers who want to extend the functionnalities of the module or want to use it for extreme cases.&lt;br /&gt;
This is not meant to guide implementers on how to implement and configure the module (see the related chapter for that).&lt;br /&gt;
&lt;br /&gt;
If you are an implementer and just want to use CustomFields with its normal functions, you can read the [[Module_CustomFields|user documentation of CustomFields]].&lt;br /&gt;
&lt;br /&gt;
You can also jump directly to [[Module_CustomFields_Cases]] wiki page for a collection of practical examples.&lt;br /&gt;
&lt;br /&gt;
= Principles =&lt;br /&gt;
&lt;br /&gt;
CustomFields is simply a wrapper for SQL databases.&lt;br /&gt;
&lt;br /&gt;
The module is based on SQL to manage the fields and most of the stuff. It uses and understands standard SQL.&lt;br /&gt;
&lt;br /&gt;
The main principles of CustomFields are:&lt;br /&gt;
&lt;br /&gt;
* one SQL column = one Dolibarr field.&lt;br /&gt;
* one SQL type = one method to show and edit the field in Dolibarr.&lt;br /&gt;
* almost everything should be configurable from the configuration file, or admin interface.&lt;br /&gt;
* be independent from Dolibarr&#039;s data structures.&lt;br /&gt;
&lt;br /&gt;
CustomFields sits besides Dolibarr, and does not mix with it, even if in the Dolibarr&#039;s interface it looks like CustomFields is embedded inside Dolibarr, in reality, CustomFields is totally independent.&lt;br /&gt;
&lt;br /&gt;
= Goals =&lt;br /&gt;
&lt;br /&gt;
This module was made to provide an easy way to manage custom sql fields, not only for users and implementers, but also for developpers to use in their own module:&lt;br /&gt;
* by easily adding the support of custom fields for your own module by using this module;&lt;br /&gt;
* by reusing custom fields from another module in your own module;&lt;br /&gt;
* by managing sql fields in your own module by using the CustomFields class (which will take care of everything from printing, editing to PDF/ODT templating).&lt;br /&gt;
&lt;br /&gt;
So this module is not only an auxiliary of other modules, but it can also be the core part of your own module by using it as your SQL query class for fields.&lt;br /&gt;
&lt;br /&gt;
= SQL structure =&lt;br /&gt;
&lt;br /&gt;
A CustomFields table needs to respect three parameters that never change:&lt;br /&gt;
&lt;br /&gt;
* table&#039;s name (format: llx_*dolibarrmodule*_customfields where *dolibarrmodule* = table_element in conf_customfields.lib.php)&lt;br /&gt;
* first column: rowid, which is the primary key for each field&lt;br /&gt;
* second column: fk_*dolibarrmodule* which is the foreign key that links every customfields record to the Dolibarr&#039;s module that is associated (invoice, order, etc...).&lt;br /&gt;
&lt;br /&gt;
Apart from the table&#039;s name and the first two special columns, every other columns are just standard sql columns that you can freely create, edit or delete as you want, from the module&#039;s administration page or from a third-party SQL management tool (such as phpMyAdmin).&lt;br /&gt;
&lt;br /&gt;
You can also create more complex sql fields: constraints, foreign keys, transformations, other sql types, etc...).&lt;br /&gt;
&lt;br /&gt;
Then the module will try to manage the sql fields you manually created the best it can:&lt;br /&gt;
* by printing appropriate input fields in the Dolibarr gui interface.&lt;br /&gt;
* by providing the developper an easy access to these fields in php (eg: $customfields-&amp;gt;cf_myfield or $object-&amp;gt;customfields-&amp;gt;cf_myfield) for any use, including PDF templates and ODT templates, or in any php script (so that you can use these fields in your own module).&lt;br /&gt;
&lt;br /&gt;
= SQL Indexes =&lt;br /&gt;
&lt;br /&gt;
The module automatically adds an index on the primary key and foreign key (first and second columns).&lt;br /&gt;
&lt;br /&gt;
To further optimize the performances of your database, and particularly the customfields tables, you may want to manually add indexes based on usage.&lt;br /&gt;
&lt;br /&gt;
Also, you may add indexes on remote tables used by constrained fields, which could optimize even more the performances since constrained fields have the worst performance drawback for CustomFields.&lt;br /&gt;
&lt;br /&gt;
= SQL Compatibility mode =&lt;br /&gt;
&lt;br /&gt;
The module relies on referential integrity checks and foreign keys of your DBMS to keep the database clean and to provide the constrained fields feature. Referential integrity checks is part of the SQL standards and should be used whenever possible, which always was a main goal for CustomFields (to be respectful of SQL standards).&lt;br /&gt;
&lt;br /&gt;
Unluckily, a few DBMS systems still don&#039;t support this critical functionality of the SQL standard, for example MySQL enables MyISAM engine by default, which doesn&#039;t support this feature.&lt;br /&gt;
&lt;br /&gt;
In these cases, you can either do two things:&lt;br /&gt;
* Switch to an engine which support referential integrity. For example with MySQL, you can enable InnoDB, which support referential integrity checks and foreign keys.&lt;br /&gt;
* Use CustomFields compatibility mode (more infos below).&lt;br /&gt;
&lt;br /&gt;
In the case your database does not support referential integrity checks, CustomFields will automatically switch to the SQL compatibility mode, which will allow you to use CustomFields even on databases where foreign keys are not possible.&lt;br /&gt;
&lt;br /&gt;
In compatibility mode, you will get nearly all CustomFields advanced features such as Constrained fields, but the customfields&#039; tables in the database won&#039;t be cleaned automatically (no delete on cascade, meaning that when you delete an object, the linked custom fields will still be kept in your database), and constrained fields won&#039;t be updated automatically on update.&lt;br /&gt;
&lt;br /&gt;
Nevertheless, these missing cleanup functionalities won&#039;t be seen by your users because they will never be accessible: on deletion of an object, the custom fields won&#039;t ever be accessible again from Dolibarr&#039;s interface, and object&#039;s ids are never changed since Dolibarr also relies on this. Thus in compatibility mode, CustomFields will act just like normally, but your database will be a bit less clean.&lt;br /&gt;
&lt;br /&gt;
= JSON extension for extra options =&lt;br /&gt;
&lt;br /&gt;
== Introduction to JSON in CustomFields ==&lt;br /&gt;
The relational model is very great to store and fetch data with a declarative language, and this is very powerful, but this model also imposes a few limitations, for example the relations must be well formatted and predefined.&lt;br /&gt;
&lt;br /&gt;
That&#039;s why we finally implemented a &#039;&#039;&#039;JSON extension&#039;&#039;&#039; to manage &#039;&#039;&#039;extra options&#039;&#039;&#039; and associate them with custom fields. This allows to add many properties (or columns if you are more used to the relational model) on-the-fly without having to predefine them prior to storing and using them.&lt;br /&gt;
&lt;br /&gt;
This allows to add an undefined number of columns/properties/extra options that will help manage special behaviours for custom fields. For example, it is currently used to allow to change the order of appearance of custom fields, and to store and manage remote table and columns datas for constrained fields on DBMS that do not support Foreign Keys checks.&lt;br /&gt;
&lt;br /&gt;
We chose JSON over XML because the library are simpler and the data footprint smaller, and because CustomFields does not need advanced features of XML like XQuery, data typing and meta structures (at least not for now).&lt;br /&gt;
&lt;br /&gt;
The other advantage is that php objects can directly be encoded into JSON without any preprocessing, and can then as straightly be decoded from JSON back into a php object.&lt;br /&gt;
&lt;br /&gt;
A simple mean to encode and decode back an object into and from a storage place, as well as allow users to easily add/remove extra options were the main requirement, and JSON (+ JSON libraries for PHP) meet this criteria.&lt;br /&gt;
&lt;br /&gt;
However, note that the extra options are now encoded as an associative array, so as to avoid any ambiguities (there&#039;s no associative array in JSON, they are converted to objects).&lt;br /&gt;
&lt;br /&gt;
== Implementation of JSON ==&lt;br /&gt;
The implementation is very simple: a table called &#039;&#039;&#039;llx_customfields_extraoptions&#039;&#039;&#039; is created when you enable the CustomFields module, and this table only contains three columns:&lt;br /&gt;
* table_name (varchar)&lt;br /&gt;
* column_name (varchar)&lt;br /&gt;
* extraoptions (blob)&lt;br /&gt;
&lt;br /&gt;
(table_name, column_name) both forms the primary key of the table, and links to columns (custom fields) of other customfields tables.&lt;br /&gt;
extraoptions is a blob, which allows to store the data as binary (as-is) without encoding (which solves a big problem of JSON which is encoding special and foreign characters, here there is no problem at all).&lt;br /&gt;
&lt;br /&gt;
When fetching custom fields, the module simply JOIN the extraoptions table, which adds an &#039;&#039;&#039;extraoptions&#039;&#039;&#039; column for each custom field in the SQL result. Then it is quite easy to just json_decode() the data returned inside the extraoptions column.&lt;br /&gt;
&lt;br /&gt;
This methodology allows to have all the advantages of a relational database, with the flexibility of a free-schema model:&lt;br /&gt;
* everything is stored inside the database (only one storage place)&lt;br /&gt;
* fixed columns allows for relational joining and optimization&lt;br /&gt;
* thus queries (fetching) are optimised&lt;br /&gt;
* at the same time, the extraoptions are automatically retrieved and associated with every custom field, and whenever needed they can be used&lt;br /&gt;
* and thus we also get a flexible model to &#039;&#039;&#039;add extra (virtual) columns whenever needed&#039;&#039;&#039; without having to alter the relational database schema&lt;br /&gt;
&lt;br /&gt;
Thus no critical information is and should be stored inside the extraoptions column because they would not be query-able, only extra options that are just used to tweak the management of those fields inside the PHP application should be stored. But this is a wonderful way to add non critical informations that still allows to develop more complex features and management for special custom fields.&lt;br /&gt;
&lt;br /&gt;
== CustomFields JSON API ==&lt;br /&gt;
&lt;br /&gt;
=== Storing extra options ===&lt;br /&gt;
&lt;br /&gt;
You can use the &#039;&#039;&#039;setExtra()&#039;&#039;&#039; method to set extra options of a custom field.&lt;br /&gt;
&lt;br /&gt;
Eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$extra = array(); // $extra should always be an associative array, it&#039;s no longer an object&lt;br /&gt;
$extra[&#039;myproperty&#039;] = &#039;Anything I want here&#039;; // set a property/extra option like this&lt;br /&gt;
$extra[&#039;category&#039;][&#039;subcategory&#039;][&#039;prop&#039;] = true; // we can set recursive properties, there&#039;s not any limit to how deep the hierarchy can be&lt;br /&gt;
&lt;br /&gt;
require_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomField($db, &#039;currentmodule&#039;);&lt;br /&gt;
$fieldid = &#039;mycustomfields&#039;; // $fieldid can either be a column_name or an ordinal_position from information_schema.columns&lt;br /&gt;
$result = $customfields-&amp;gt;setExtra($fieldid, $extra); // store the extra options inside the database&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can also use addCustomField() or updateCustomField() with an $extra array, and these functions will also call the setExtra() method.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: the extra options you set are &#039;&#039;&#039;appended&#039;&#039;&#039; to the ones already stored in the database. However, you can also overwrite previous options, just use the same name of property but with a different value (the replacement is recursive, you can do it at any level in the hierarchy).&lt;br /&gt;
&lt;br /&gt;
=== Fetching extra options ===&lt;br /&gt;
&lt;br /&gt;
Extra options are automatically fetched along with a custom field structure, decoded, and stored inside the extra sub-property of any field object, or CustomFields object.&lt;br /&gt;
&lt;br /&gt;
When fetched, extra options are stored as a sub-object for the current field object (which itself is stored as a subobject of the CustomFields object if caching is enabled).&lt;br /&gt;
&lt;br /&gt;
Eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
require_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomField($db, &#039;currentmodule&#039;);&lt;br /&gt;
$fields = fetchAllFieldsStruct(); // fetching fields structure will automatically fetch extra options at the same time. This works with fetchAllFieldsStruct() and fetchFieldStruct()&lt;br /&gt;
&lt;br /&gt;
// Now that you fetched the fields structure, you can just access the extra options sub-properties:&lt;br /&gt;
print_r($fields-&amp;gt;mycustomfield-&amp;gt;extra); // will print all extra options for the field mycustomfield&lt;br /&gt;
print_r($customfields-&amp;gt;fields-&amp;gt;mycustomfield-&amp;gt;extra); // you can also access the same field(s) directly from the CustomFields object if caching is enabled (default behaviour)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Modifying extra options ===&lt;br /&gt;
&lt;br /&gt;
You can just combine the two previous points to edit extra options of a custom field: just load a custom field, edit on-the-fly the extra property with PHP code, and then store it back directly into the database:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
require_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomField($db, &#039;currentmodule&#039;);&lt;br /&gt;
$fields = fetchAllFieldsStruct();&lt;br /&gt;
&lt;br /&gt;
$fields-&amp;gt;mycustomfield-&amp;gt;extra[&#039;newproperty&#039;] = &#039;This is a new property&#039;; // add a new property over the old extra options sub-object&lt;br /&gt;
$fields-&amp;gt;mycustomfield-&amp;gt;extra[&#039;mycategory&#039;] = &#039;Overwrite the old category&#039;; // this will replace the previous value of &#039;mycategory&#039; extra option&lt;br /&gt;
&lt;br /&gt;
$customfields-&amp;gt;setExtra(&#039;mycustomfield&#039;, $fields-&amp;gt;mycustomfield-&amp;gt;extra); // store the modified extra options back into the database for future usage&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= CustomFields API =&lt;br /&gt;
&lt;br /&gt;
== Introduction to CustomFields API ==&lt;br /&gt;
&lt;br /&gt;
This chapter will cover the basics on how to manually and concretely use the CustomFields class to create/update/fetch/manage custom fields.&lt;br /&gt;
&lt;br /&gt;
This will give you a better understanding of how CustomFields works, as well as giving you insights on how to use it for your own applications.&lt;br /&gt;
&lt;br /&gt;
Important note: contrary to the Facade API, the core CustomFields class use columns names without any prefix, so don&#039;t try to use the prefix, it won&#039;t work! For example: if your field is named &#039;mycustomfield&#039; and prefix is &#039;cf_&#039;, when you use the Facade API you should use &#039;$object-&amp;gt;$path-&amp;gt;cf_mycustomfield&#039; but when you use the core CustomFields class directly, $fieldname = &#039;mycustomfield&#039;.&lt;br /&gt;
&lt;br /&gt;
== Prerequisite to use the CustomFields class ==&lt;br /&gt;
&lt;br /&gt;
Before using the CustomFields class, you first must instanciate it. This is a necessary preliminary step before using any of the methods that will be covered inside this chapter, and thus will not be repeated, only shown once here.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Include the CustomFields class (which contains the main API)&lt;br /&gt;
require_once(dirname(__FILE__).&#039;/../class/customfields.class.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Instanciate a CustomFields object&lt;br /&gt;
global $db; // the database handler created by Dolibarr&lt;br /&gt;
$table_element = &#039;currentmodule&#039;; // where $table_element is the database&#039;s table name for the module, without the Dolibarr prefix (eg: for &#039;llx_facture&#039;, table_element is &#039;facture&#039;). You can also find it inside the $object properties (just print_r() it).&lt;br /&gt;
$customfields = new CustomFields($db, $table_element); // instanciate the CustomFields class inside $customfields object&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
CustomFields keeps track of errors that can happen when executing methods and SQL queries inside the &#039;&#039;&#039;error&#039;&#039;&#039; and &#039;&#039;&#039;errors&#039;&#039;&#039; properties, which you can print with the printErrors() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Do something with CustomFields before&lt;br /&gt;
&lt;br /&gt;
if (count($customfields-&amp;gt;errors) &amp;gt; 0) { // If there is at least one error&lt;br /&gt;
    $customfields-&amp;gt;printErrors(); // print errors&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// You can also print your own error message, in two manners:&lt;br /&gt;
&lt;br /&gt;
// 1- submit your own message&lt;br /&gt;
$customfields-&amp;gt;printErrors(&#039;Your own error message here&#039;); // this will print ONLY your error message, using the Dolibarr&#039;s AJAX error dialog (standard way of printing errors)&lt;br /&gt;
&lt;br /&gt;
// 2- append your message in the customfields errors&lt;br /&gt;
$customfields-&amp;gt;errors[] = &#039;Your own error message to be appended with other CustomFields messages&#039;;&lt;br /&gt;
$customfields-&amp;gt;printErrors(); // Here your error message will be appended, thus all error messages will be printed. This allows you for example to add more intelligible informations on a specific error.&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== General purpose SQL methods ==&lt;br /&gt;
&lt;br /&gt;
CustomFields provide an interface to easily execute various common SQL requests, as well as your own custom SQL requests.&lt;br /&gt;
&lt;br /&gt;
* fetchAllTables() fetch a list of all the tables in the database&lt;br /&gt;
* fetchPrimaryField($table) fetch the column_name (and only the column_name!) of the primary field of a table. You can then use fetchFieldStruct()  with the column_name to get any information you want.&lt;br /&gt;
* fetchAny($columns, $table, $where=&#039;&#039;, $orderby=&#039;&#039;, $limitby=&#039;&#039;) allows you to issue any simple SQL query command, for example fetchAny(&#039;*&#039;, &#039;sometable&#039;) is equivalent to SELECT * FROM sometable; (but then everything is managed automatically and result is returned to you)&lt;br /&gt;
* executeSQL($sql, $eventname) to execute any SQL query you want, this will return you a resource (and NOT a processed nuplet like fetchAny!). $eventname can be anything, it&#039;s just for Dolibarr logging facility.&lt;br /&gt;
* executeMultiSQL($sql, $eventname) to execute multiple SQL queries&lt;br /&gt;
* fetchFieldStruct($id=null, $nohide=false, $table=null, $cachepath=null) allows you to fetch the field&#039;s SQL structure of not only custom fields, but of any database&#039;s column using the $table variable. Eg: fetchFieldStruct(&#039;mycolumn&#039;, true, &#039;mytable&#039;);&lt;br /&gt;
* probeTable($table) helps you to check if a table exists in the database&lt;br /&gt;
* fetch_object($resource) will return a PHP object from a $resource, similarly to mysql_fetch_object() but with two advantages: it works on several DBMS (not only mysql), and it always return lowercase properties names (else depending on the configuration of the DBMS, the request can return uppercase or mixed case properties names).&lt;br /&gt;
* escape() and reverse_escape() are similare to mysql_real_escape but is reversible and DBMS-independent (so you can use it with other DBMSes).&lt;br /&gt;
&lt;br /&gt;
== Managing CustomFields structure ==&lt;br /&gt;
&lt;br /&gt;
The first step into CustomFields is to create and manage the structure of a custom field.&lt;br /&gt;
&lt;br /&gt;
=== Creating a custom field definition ===&lt;br /&gt;
&lt;br /&gt;
To create a custom field, you can use the addCustomField() method.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;addCustomField($fieldname, $type, $size, $nulloption, $defaultvalue = null, $constraint = null, $customtype = null, $customdef = null, $customsql = null, $fieldid = null, $extra = null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will take care of forging the (complicated) SQL query with the forgeSQLCustomField() method, and will return you an error if there was a problem.&lt;br /&gt;
&lt;br /&gt;
=== Updating a custom field definition ===&lt;br /&gt;
&lt;br /&gt;
To update a custom field, you can use the updateCustomField() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;&lt;br /&gt;
updateCustomField($fieldid, $fieldname, $type, $size, $nulloption, $defaultvalue, $constraint = null, $customtype = null, $customdef = null, $customsql = null, $extra = null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: the updateCustomField() and addCustomField() methods can both be used since updateCustomField() is in fact an alias for addCustomField(). CustomFields will automatically detect if a custom field already exists, and update it if that&#039;s true, or create it.&lt;br /&gt;
&lt;br /&gt;
Note2: updating a custom field will automatically take care of the constraints and foreign keys on its own, thus you can change the foreign key, or remove it altogether if set to null.&lt;br /&gt;
&lt;br /&gt;
=== Delete a custom field definition ===&lt;br /&gt;
&lt;br /&gt;
You can easily delete a custom field by column_name or by ordinal_position:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;deleteCustomField($id);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: constraints and foreign keys will also be deleted automatically so you shouldn&#039;t encounter any error.&lt;br /&gt;
&lt;br /&gt;
=== Fetch one custom field&#039;s SQL structure ===&lt;br /&gt;
&lt;br /&gt;
To get the SQL structure of a custom field, which can be quite useful to know for example its datatype or if it has a foreign key, or accepts null values, you can use the fetchFieldStruct() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
fields = $customfields-&amp;gt;fetchFieldStruct($id=null, $nohide=false, $table=null, $cachepath=null); // where $id can be a column_name, an ordinal_position from information_schema.columns or null if you want to fetch all the columns at once&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method will either return null, either a single field object, either an array on field objects.&lt;br /&gt;
&lt;br /&gt;
Note: this function will use caching by default to accelerate future results, but you can disable it, or even change the default cache path. For more informations please read the comments in the source code.&lt;br /&gt;
&lt;br /&gt;
=== Fetch several custom fields&#039; SQL structures ===&lt;br /&gt;
&lt;br /&gt;
Use the fetchAllFieldsStruct() method, which is an alias for fetchFieldStruct():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;fetchAllFieldsStruct($nohide=false, $table=null, $cachepath=null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: this method will always return an array of fields objects, even if only one is found (for more consistency), and this is the main difference with fetchFieldStruct(null).&lt;br /&gt;
&lt;br /&gt;
== Managing custom fields records ==&lt;br /&gt;
&lt;br /&gt;
Now that you know how to create and manage the structure of custom fields, we can proceed on to managing the records.&lt;br /&gt;
&lt;br /&gt;
=== Creating a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Simply use the create() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;create($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
$object must be an object containing as its properties the customfields data.&lt;br /&gt;
&lt;br /&gt;
For example, if you have a field named mycustomfield, your object must have $object-&amp;gt;mycustomfields defined.&lt;br /&gt;
&lt;br /&gt;
This method will store in the database ALL the custom fields that were created/edited and with a valid value (eg: an empty value for a field that does not accept null value is invalid) AT ONCE! So you can modify all the columns of one record at once, but not several records at once.&lt;br /&gt;
&lt;br /&gt;
Concretely, this is very useful with Dolibarr, because modules tends to store everything inside a single object generally called $object, and CustomFields tries to hook onto $object, so that you can just pass $object as is to create() and it will save all the custom fields.&lt;br /&gt;
&lt;br /&gt;
But you can also create an empty object with $object = new stdClass(); and then manually fill the custom fields records with whatever you want, and then call the create() method, this will work as well!&lt;br /&gt;
&lt;br /&gt;
You can also just modify only one column of one record, by just specifying one custom field column_name in your $object. The other custom fields that are not specified won&#039;t be created/modified.&lt;br /&gt;
&lt;br /&gt;
=== Updating a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Use the update() method, which is an alias for the create() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;update($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As noted above, update() is an alias of the create() method, which means that CustomFields will try to detect by itself if a record needs to be updated or created.&lt;br /&gt;
&lt;br /&gt;
=== Deleting a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Use the delete() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;delete($id); // where $id is = to fk_moduleid column, NOT rowid&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fetching a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Just use the fetch() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$records = $customfields-&amp;gt;fetch($id=null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where $id can either be = fk_moduleid (NOT rowid), or null to fetch all the records at once.&lt;br /&gt;
&lt;br /&gt;
This method will either return null, a single record object, or an array of records objects.&lt;br /&gt;
&lt;br /&gt;
=== Fetching all custom fields&#039; records ===&lt;br /&gt;
&lt;br /&gt;
Use the fetchAll() method, which is an alias for fetch():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$records = $customfields-&amp;gt;fetchAll();&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: this method will always return an array of records object, for more consistency (and this is the main difference with fetch(null) ).&lt;br /&gt;
&lt;br /&gt;
=== Fetching referenced records of a constrained field ===&lt;br /&gt;
&lt;br /&gt;
A constrained custom field points to another database table, and a value for this constrained field is the rowid of a record in the referenced table.&lt;br /&gt;
&lt;br /&gt;
Thus, the principal purpose of a constrained custom field is not the value of the field in itself (it&#039;s simply an integer), but rather the other fields available from the referenced table, the constrained field being in fact just a pointer.&lt;br /&gt;
&lt;br /&gt;
To fetch the remote record from the referenced table, use the fetchReferencedValuesList() of the CustomFields class:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$fkrecord = fetchReferencedValuesList($field, $id=null, $where=null, $allcolumns=false);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
&lt;br /&gt;
* $field is a custom field object (fetched using fetchFieldStruct() or fetchAllFieldsStruct()),&lt;br /&gt;
* $id is the rowid of the record to get, just like fetch() and fetchAll(). Can be null to fetch all remote records (or to use the $where clause).&lt;br /&gt;
* $where is a string without the WHERE clause (eg: &amp;quot;fk_facture=1&amp;quot;) to get finer control. In this case, you can use $id or set $id=null depending on your purpose. This is mainly a private argument to allow for cascade option.&lt;br /&gt;
* $allcolumns if false, it will fetch only the required columns to do the smart value substitution. If true, it will fetch all remote columns (useful for ODT substitution). If an array of strings, each string will be a column to fetch from the remote database.&lt;br /&gt;
&lt;br /&gt;
Note that this function is NOT recursive (it will fetch the records from the referenced table, and that&#039;s all, it won&#039;t fetch the custom fields of the referenced table nor recursively fetch the remote constrained custom fields). To do recursive fetching, see in the Facade API.&lt;br /&gt;
&lt;br /&gt;
Note2: this function automatically manages Smart Value Substitution depending of the $field-&amp;gt;column_name.&lt;br /&gt;
&lt;br /&gt;
=== Recursive fetching referenced records of a constrained field ===&lt;br /&gt;
&lt;br /&gt;
If you want to recursively fetch a constrained field (eg: constrainedfield1-&amp;gt;ref_table-&amp;gt;constrainedfield2-&amp;gt;ref_table2-&amp;gt;...), you need another function called fetchReferencedValuesRec(). Technically, this function is in the Facade API (inside /customfields/lib/customfields_aux.lib.php), but it isn&#039;t meant to be used by lambda users (contrary to customfields_fill_object()), but is mainly a helper function for the rest of the facade API.&lt;br /&gt;
&lt;br /&gt;
fetchReferencedValuesRec() works very similarly to fetchReferencedValuesList(), but it will work recursively.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$fkrecord = fetchReferencedValuesRec($customfields, $field, $id, $recursive=true)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where $customfields is the CustomFields instance for the module you are working on, $field is the constrained field you want to fetch, $id is the record&#039;s id you want to fetch, and $recursive enables or disables the recursion.&lt;br /&gt;
&lt;br /&gt;
Note that this function automatically manages Smart Value Substitution (and also recursively).&lt;br /&gt;
&lt;br /&gt;
Note2: This function tries to automatically avoid an infinite recursion loop by using a $blacklist: for a given constrained custom field, a table won&#039;t be visited twice. This means that for example you can do: invoice-&amp;gt;user-&amp;gt;socpeople-&amp;gt;invoice, this will work and every referenced fields will be fetched, but it will automatically stop the recursion at the second invoice, because else it could continue in a loop to user, socpeople, invoice, user, socpeople, etc.&lt;br /&gt;
&lt;br /&gt;
Note3: when adding recursively referenced fields, the key in the returned array will be prefixed by the name of the custom field that led to those remote fields. Thus, you can have something like &amp;quot;cf_myfield1_cf_remotefield1_cf_remotefield2&amp;quot; if you have a constrained field cf_myfield1-&amp;gt;cf_remotefield1-&amp;gt;cf_remotefield2.&lt;br /&gt;
&lt;br /&gt;
== Input forms for custom fields ==&lt;br /&gt;
&lt;br /&gt;
The API provides some neat ways to automatically manage the input forms for users to input new values, with the best adapted HTML form based on the custom field&#039;s data type.&lt;br /&gt;
&lt;br /&gt;
The following methods will print input fields that are best adapted to the SQL data type of your custom fields as well as figure out the possible values and even format them (for example: a not null enum will be printed as a HTML select list without an empty option; fetch automatically remote columns for constrained fields and show them in a select).&lt;br /&gt;
&lt;br /&gt;
So you don&#039;t have to bother how your data will be presented to the user nor how it will be processed.&lt;br /&gt;
&lt;br /&gt;
=== Printing an input form adapted to the field&#039;s data type ===&lt;br /&gt;
&lt;br /&gt;
showInputField() is a sort of converter customfieldstructure -&amp;gt; HTML input field.&lt;br /&gt;
&lt;br /&gt;
Use the &#039;&#039;&#039;showInputField()&#039;&#039;&#039; method to print the right input form for a custom field.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;showInputField($field, $currentvalue=null, $moreparam=&#039;&#039;, $ajax_php_callback=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
showInputField() takes 4 parameters:&lt;br /&gt;
&lt;br /&gt;
* $field is a CustomFields structure object (eg: returned by fetchFieldStruct()) and is necessary to detect the right datatype and the possible values.&lt;br /&gt;
* $currentvalue is the current value if you want to preselect a value (for example if the user already defined a value, you can reload it here). This is optional, in case it is not defined, either a null/empty value will be chosen, or either the default value defined when creating the custom field.&lt;br /&gt;
* $moreparam is used to add more HTML attributes, if you want for example to add some css styling or whatever you want.&lt;br /&gt;
* $ajax_php_callback is optional and allows to specify the relative url to the php callback script. If specified, an AJAX script will be automatically attached to this field (see AJAX-PHP callback below).&lt;br /&gt;
&lt;br /&gt;
showInputField() only prints ONE HTML input field, thus you have to enclose it/them by your own HTML form.&lt;br /&gt;
&lt;br /&gt;
eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print &#039;&amp;lt;form method=&amp;quot;get&amp;quot; action=&amp;quot;?&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
print &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;customtoken&amp;quot; value=&amp;quot;ABCDEFGH&amp;quot; /&amp;gt;&#039;; // example of a custom hidden HTML input field, that can be used to pass a token for example&lt;br /&gt;
// Loop over each custom fields (you have to load them before with $customfields-&amp;gt;fetchAllFieldsStruct()&lt;br /&gt;
foreach ($customfields-&amp;gt;fields as $field) {&lt;br /&gt;
    print $customfields-&amp;gt;showInputField($field, $currentvalue=null, $moreparam=&#039;&#039;);&lt;br /&gt;
}&lt;br /&gt;
print &#039;&amp;lt;/form&amp;gt;&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can also store the resulting HTML input field and process it by a regexp for example:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$myhtmlfield = $customfields-&amp;gt;showInputField($field, $currentvalue=null, $moreparam=&#039;&#039;);&lt;br /&gt;
preg_replace($myhtmlfield, ..., ...);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: for constrained fields, if the name follows the convention of &#039;&#039;&#039;Smart Value Substitution&#039;&#039;&#039; (ie: remote columns names are included in the custom field&#039;s name), showInputField() will &#039;&#039;&#039;automatically fetch the remote values, format them, and present them&#039;&#039;&#039; in a HTML select.&lt;br /&gt;
&lt;br /&gt;
=== Printing an input form for a custom field ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;showInputForm()&#039;&#039;&#039; is a function used to print a single (and only one!) custom field with an adapted HTML input field, but enclosed by a HTML form. This is used for example when you want to allow to edit a single custom field but not the others.&lt;br /&gt;
&lt;br /&gt;
showInputForm() is in fact a wrapper for showInputField(), thus it will call showInputField() and simply enclose it by a form.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;showInputForm($id, $field, $currentvalue=null, $idvar=&#039;id&#039;, $page=null, $moreparam=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== AJAX-PHP callback ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;showInputFieldAjax()&#039;&#039;&#039; will output an AJAX script (using jQuery) to send and receive data to and from a specified php script. The AJAX script will automatically manage HTML form input&#039;s updating as long as the received data follows some convention.&lt;br /&gt;
&lt;br /&gt;
An AJAX script can be attached to any field (including non-CustomFields) by using the method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;showInputFieldAjax($id, $php_callback_url, $on_func=&amp;quot;change&amp;quot;, $request_type=&amp;quot;post&amp;quot;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
* $id is the HTML id of the field you want to attach the AJAX to.&lt;br /&gt;
* $phpcallback the relative path from Dolibarr&#039;s htdocs root folder to the php script that will receive and send back data.&lt;br /&gt;
* $on_func is the Javascript or jQuery event that should trigger the AJAX script (by default on change).&lt;br /&gt;
* $request_type is the HTML query type that will be used to send the data to the PHP callback (either &amp;quot;get&amp;quot; or &amp;quot;post&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
The AJAX script will automatically manage sending/receiving of data:&lt;br /&gt;
&lt;br /&gt;
* automatically send all form&#039;s inputs values, as well as the current calling field&#039;s name and value. The sending happens on change by default, but another event can be set using the $on_event argument. Data will be sent using standard GET or POST.&lt;br /&gt;
* Upon reception of data from the php script, the AJAX script will automatically parse the data and update the HTML fields.&lt;br /&gt;
&lt;br /&gt;
The PHP callback script must send back to AJAX the data in JSON encoded format, and with with a specific format:&lt;br /&gt;
&lt;br /&gt;
* the data must be an associative array, where each entry&#039;s key is the name (not ID!) of an HTML field to update.&lt;br /&gt;
* each entry&#039;s value is an associative array, where each sub-entry&#039;s key is the action to do. Available actions: &#039;options&#039;, &#039;value&#039;, &#039;html&#039;, &#039;alert&#039;.&lt;br /&gt;
* each sub-entry&#039;s value is the value for this action and HTML field.&lt;br /&gt;
&lt;br /&gt;
Thus, this approach allows to generically support a wide range of updating actions, managed automatically.&lt;br /&gt;
&lt;br /&gt;
You, the implementer, have just to edit the PHP callback script and return an associative array defining what action you want for each field you want to update. This is exactly what has been implemented in the Custom AJAX Functions library, which simplifies even more your job by managing automatically the step described here (generating AJAX and formatting data), so that you can focus on &#039;&#039;&#039;making the data&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Printing custom fields in documents ==&lt;br /&gt;
&lt;br /&gt;
The API can also automatically manage the printing and formatting of custom fields in your ODT, PDF and HTML documents.&lt;br /&gt;
&lt;br /&gt;
=== Printing in HTML ===&lt;br /&gt;
&lt;br /&gt;
You can either just print the value as-is, or pass them through the printField() or simpleprintField() function to format them (very useful for constrained fields) - see below.&lt;br /&gt;
&lt;br /&gt;
=== Printing in documents ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;printField()&#039;&#039;&#039; is used to properly print and translate a custom field&#039;s record&#039;s value. Generally, you can just print the custom field&#039;s value as-is, but in a few cases (eg for boolean types, or constrained fields), using the printField() method will more properly print the value.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;printField($field, $value, $outputlangs=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
* $field is the custom field&#039;s SQL structure as an object (eg: returned by fetchFieldStruct()).&lt;br /&gt;
* $value is the custom field&#039;s value for the current record (eg: $customfields-&amp;gt;fields-&amp;gt;myfield).&lt;br /&gt;
* $outputlangs is the Translate object for the target language.&lt;br /&gt;
&lt;br /&gt;
For example, with a constrained field with Smart Value Substitution, instead of printing the id like 1, it will print the full name of user like FirstName Name.&lt;br /&gt;
&lt;br /&gt;
This method will also translate a value if a translation is found in the language files.&lt;br /&gt;
&lt;br /&gt;
Another method &#039;&#039;&#039;printFieldPDF()&#039;&#039;&#039; can be used to properly print a value for a PDF (properly encoding the characters).&lt;br /&gt;
&lt;br /&gt;
Powertip: this function can also be used to get the Smart Value Substituted value of the field (instead of printing the rowid) in the field is constrained.&lt;br /&gt;
&lt;br /&gt;
=== Simple printing in documents ===&lt;br /&gt;
&lt;br /&gt;
If you don&#039;t want to have to manually fetch the structure of every custom field whose you want to print the value, you can then use a simplifier method called &#039;&#039;&#039;simpleprintField()&#039;&#039;&#039; which will take as input a custom field name instead of a custom field SQL structure object.&lt;br /&gt;
&lt;br /&gt;
This method is just a simplifier (Facade) wrapper for the printField() method, and thus will have the same advantages (fetch constrained fields, values translation, etc.).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;simpleprintField($fieldname, $value, $outputlangs=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
* $fieldname is the custom field&#039;s name (eg: &amp;quot;myfield&amp;quot;).&lt;br /&gt;
* $value is the custom field&#039;s value for the current record (eg: $customfields-&amp;gt;fields-&amp;gt;myfield).&lt;br /&gt;
* $outputlangs is the Translate object for the target language.&lt;br /&gt;
&lt;br /&gt;
Another method &#039;&#039;&#039;simpleprintFieldPDF()&#039;&#039;&#039; can be used to properly print a value for a PDF (properly encoding the characters).&lt;br /&gt;
&lt;br /&gt;
Powertip: this function can also be used to get the Smart Value Substituted value of the field (instead of printing the rowid) in the field is constrained.&lt;br /&gt;
&lt;br /&gt;
=== Find the label ===&lt;br /&gt;
&lt;br /&gt;
Finding the label of a custom field might not be as simple as it seems. First, values of properties objects may be easily used, but their name (key) is a worst job. Secondly, custom fields&#039; labels can be translated, and the following functions are expressly made for that purpose. Thirdly, for PDF and some other documents you have to encode the characters if you want them to properly be drawn.&lt;br /&gt;
&lt;br /&gt;
So if you want to print a custom field&#039;s label with translation (multilingual) support, just use the &#039;&#039;&#039;findLabel()&#039;&#039;&#039; method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$translatedfieldname = $customfields-&amp;gt;findLabel($fieldname, $outputlangs = &#039;&#039;); // where $fieldname is just a custom field label (eg: cf_mycustomfield or mycustomfield, both will work) and $outputlangs is the target language object (see the Translate class native of Dolibarr)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As for the other printing methods, you also have the &#039;&#039;&#039;findLabelPDF()&#039;&#039;&#039; wrapper method to easily print labels that with characters encoded for PDF.&lt;br /&gt;
&lt;br /&gt;
= Facade API =&lt;br /&gt;
&lt;br /&gt;
The Facade API was made to ease the usage of the CustomFields class with the Dolibarr system. It provides a neat way to do many operations with single commands, and will automatically manage the loading of the CustomFields class, and its usage with many optimizations.&lt;br /&gt;
&lt;br /&gt;
== customfields_fill_object() ==&lt;br /&gt;
&lt;br /&gt;
You can load the Facade API and then use the &#039;&#039;&#039;customfields_fill_object()&#039;&#039;&#039; to automatically fetch the fields structures and the records, and also automatically link to remote fields when a constrained field is found. This is an all-in-one solution to easily fetch CustomFields datas, and is also optimized to fully use caching and other special properties of the CustomFields class.&lt;br /&gt;
&lt;br /&gt;
Concretely, you just have to include the Facade API, then pass the customfields_fill_object() with a Dolibarr $object, and the Facade API will return a CustomFields object with appended the custom fields records and also the custom fields&#039; SQL structures.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$customfields = customfields_fill_object($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* $object will then include the custom fields records associated with this object (eg: $object-&amp;gt;customfields-&amp;gt;cf_yourfield).&lt;br /&gt;
* $customfields will be a CustomFields object containing the fields structures (eg: $customfields-&amp;gt;fields-&amp;gt;yourfield).&lt;br /&gt;
&lt;br /&gt;
You can also call any CustomFields function if you need to using the $customfields object, eg: $customfields-&amp;gt;simpleprintField($object-&amp;gt;customfields-&amp;gt;cf_yourfield);&lt;br /&gt;
&lt;br /&gt;
Note: $object can also be a dummy object containing only two properties that are required: $object-&amp;gt;id and $object-&amp;gt;table_element.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will now describe the full specification of this function:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$customfields = customfields_fill_object(&amp;amp;$object,$fromobject = null, $outputlangs = null, $prefix = null,$pdfformat = false);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* $object is the object where the customfields will be saved&lt;br /&gt;
* $fromobject is the object from where the customfields will be fetched (this can be a different module than $object, or can be null to be the same as $object)&lt;br /&gt;
* $outputlangs is the target language for translation of fields value (if empty, the default current global translation object will be used)&lt;br /&gt;
* $prefix allows you to save the custom fields in a sub-object. By default, when $prefix=null, custom fields are saved in the path $object-&amp;gt;customfields, but when you specify a prefix, custom fields will be saved inside $object-&amp;gt;customfields-&amp;gt;$prefix. This may be useful if you want to store multiple object&#039;s custom fields or records in the same $object.&lt;br /&gt;
* $pdfformat defines if you want a special formatting for the values: if null, there&#039;s no formatting, if false, there is a simple formatting (eg: translation of fields&#039; values), if true then the formatting will be adapted for PDF templating.&lt;br /&gt;
&lt;br /&gt;
For more informations on pratical usages of this function, please [[Module_CustomFields#Implementing_in_php_code_.28dolibarr_core_modules_or_your_own_module.29|read the user documentation on how to implement CustomFields in php code]] or [[Module_CustomFields_Cases#Linking_Dolibarr_objects_from_two_different_modules|read an example case of linking two objects of different modules together]].&lt;br /&gt;
&lt;br /&gt;
== customfields_fill_object_lines() ==&lt;br /&gt;
&lt;br /&gt;
Similar to customfields_fill_object() for normal custom fields, there is also a &#039;&#039;&#039;customfields_fill_object_lines()&#039;&#039;&#039; to fetch custom fields associated with lines of products/services. This function is also optimized and it is strongly advised to use it and to not try to fetch manually this kind of custom fields (ie: associated with lines of products/services rather than objects), because there are a lot of optimizations going on behind the scene and that are very hard to do manually.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields for lines of products/services (you can then access them by doing $object-&amp;gt;customfields-&amp;gt;lines-&amp;gt;$lineid-&amp;gt;cf_yourfield)&lt;br /&gt;
$customfields = customfields_fill_object_lines($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Requirements: $object can be a dummy object but must contain at least 2 properties: $object-&amp;gt;id and $object-&amp;gt;table_element_line.&lt;br /&gt;
&lt;br /&gt;
For more informations on pratical usages of this function, please [[Module_CustomFields#Implementing_in_php_code_.28dolibarr_core_modules_or_your_own_module.29|read the user documentation on how to implement CustomFields in php code]].&lt;br /&gt;
&lt;br /&gt;
= Architecture =&lt;br /&gt;
Here is a full list of the CustomFields packaged files with a short description (for a more in-depth view just crawl the source files, they are full of comments):&lt;br /&gt;
&lt;br /&gt;
== Core files ==&lt;br /&gt;
files that are necessary for the CustomFields to work, they contains the core functions&lt;br /&gt;
&lt;br /&gt;
* /customfields/admin/customfields.php --- Administrator&#039;s configuration panel : this is where you create and manage the custom fields definitions&lt;br /&gt;
* /customfields/class/actions_customfields.class.php --- Hooks class : used to hook into Dolibarr core modules without altering any core file (can be used to hook into your own modules too). Also used to preprocess the parameters before calling the printforms library.&lt;br /&gt;
* /customfields/class/customfields.class.php --- Core class : every database action is made here in this class. You can find some printing functions because they are very generic.&lt;br /&gt;
* /customfields/conf/conf_customfields.lib.php --- Configuration file : contains the main configurable variables to adapt CustomFields to your needs or to expand its support to other modules and more native sql types.&lt;br /&gt;
* /customfields/conf/conf_customfields_func.lib.php --- Auxiliary functions library to read and manage config variables&lt;br /&gt;
* /customfields/core/modules/modCustomFields.class.php --- Dolibarr&#039;s module definition file : this is a core file necessary for Dolibarr to recognize the module and to declare the hooks to Dolibarr (but it does not store anything else than meta-informations).&lt;br /&gt;
* /customfields/core/substitutions/functions_customfields.lib.php --- CustomFields substitution class for ODT generation : necessary to support customfields tags in ODT files&lt;br /&gt;
* /customfields/core/triggers/interface_50_modCustomFields_SaveFields.class.php --- Core triggers file : this is where the actions on records are managed. This is an interface between other modules and CustomFields management. You should not modify this file but you should add your triggers in $triggersarray inside conf_customfields.lib.php, but if you really have a special need and know what you do, you can edit this file to directly add your own triggers. Also, if CustomFields are shown in the forms but cannot be modified nor saved, then probably this trigger file is not detected by Dolibarr (maybe you can try to lower the number 50 to raise the priority?).&lt;br /&gt;
* /customfields/fields/customfields_fields_extend.lib.php.example --- Overloading functions library with users functions: this is where implementers can use their own php code to manage the custom fields they want (one function per action and per field).&lt;br /&gt;
* /customfields/fields/customfields_fields_ajax_custom.lib.php.example --- Custom AJAX functions library with users functions: this is where implementers can use their own php code to manage the custom fields cascading and AJAX calls, similarly to the Overloading functions library (one function per action and per field).&lt;br /&gt;
* /customfields/langs/code_CODE/customfields.lang --- Core language file : this is where you can translate the admin config panel (data types names, labels, descriptions, etc.)&lt;br /&gt;
* /customfields/langs/code_CODE/customfields-user.lang --- User defined language file : this is where you can store the labels and values of your custom fields (see the related chapter)&lt;br /&gt;
* /customfields/lib/customfields_aux.lib.php --- Simplifier library to easily use CustomFields class to populate an $object with custom fields datas (Facade design pattern). This is what you should use for most of your needs.&lt;br /&gt;
* /customfields/lib/customfields_printforms.lib.php --- Core printing library for records : contains only printing functions, there&#039;s not really any core functions inside but it allows to manage the printing of the custom fields records and their editing.&lt;br /&gt;
* /customfields/lib/customfields_ajax_wrapper.lib.php --- AJAX wrapper library to automatically manage cascading at objects creation. This isn&#039;t really necessary for CustomFields to function, this is more an addition to allow dynamical AJAX updating, but CustomFields core functionalities can work without this library.&lt;br /&gt;
* /customfields/sql/*.sql --- Unused (the tables are created directly via a function in the customfields.class.php) - but the sql files are still valid, they were used as squeletton in the first designs.&lt;br /&gt;
&lt;br /&gt;
== PDFTest module ==&lt;br /&gt;
optional module to append a page listing all custom fields values on newly generated PDF documents. To be used to test if custom fields are working correctly before making one&#039;s own template.&lt;br /&gt;
&lt;br /&gt;
* /customfieldspdftest/class/actions_customfieldspdftest.class.php --- Main functions library and hooks class, to hook onto pdf generation. Contains the PDFTest functions.&lt;br /&gt;
* /customfieldspdftest/core/modules/modCustomFieldsPDFTest.class.php --- Module descriptor for Dolibarr&lt;br /&gt;
&lt;br /&gt;
== Runtime outline ==&lt;br /&gt;
&lt;br /&gt;
Here is an outline of how CustomFields works: what functions will be called and why:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At admin page:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* No external function call (apart for some icons or needed admin functions like for tabs), everything that is done are sql queries via CustomFields class (to fetch and modify the sql columns in the customfields tables).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At printing:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Module&#039;s hook formObjectOptions calls Dolibarr&#039;s hookmanager&lt;br /&gt;
* Hookmanager calls CustomFields /class/actions_customfields.class.php, function formObjectOptions() of CustomFields&lt;br /&gt;
* Preprocess a few parameters (mainly the $action) and calls customfields_print_forms()&lt;br /&gt;
* customfields_print_forms() preprocess all the hook&#039;s parameters and assimilate them with the CustomFields&#039;s configuration parameters (there&#039;s no connection to the database at this stage, only parameters preprocessing), then calls /lib/customfields_printforms.lib.php, functions customfields_print_creation_form() or customfields_print_datasheet_form&lt;br /&gt;
* both customfields_print_creation_form() lookup the database using CustomFields class and prints empty custom fields; customfields_print_datasheet_form() lookup the database using CustomFields class, fetch the values of the custom fields and prints them;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At edition:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Same steps as &#039;&#039;&#039;At printing&#039;&#039;&#039;&lt;br /&gt;
* then customfields_print_datasheet_form() process the custom field being edited differently than the others: if editing, it prints the html input field (to enter a new value), or if saving it just saves the data into the database by calling the CUSTOMFIELDS_MODIFY trigger and then continue to print the other fields normally.&lt;br /&gt;
* CUSTOMFIELDS_MODIFY trigger calls $customfields-&amp;gt;update($newrecord);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At creation (saving):&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Module&#039;s creation trigger MYMODULE_CREATE calls Dolibarr&#039;s run_trigger()&lt;br /&gt;
* Dolibarr run_trigger() calls CustomFields trigger file /core/trigger/interface_50_modCustomFields_SaveFields.class.php, function run_trigger()&lt;br /&gt;
* CustomFields&#039;s run_trigger() assimilate the given parameters with the CustomFields&#039;s config parameters, and if the trigger is found in $triggersarray, then the appropriate action is taken (by default: _CREATE or another standard action if recognized), if not in $triggersarray, the function will still try to automatically detect the appropriate action by detecting MYMODULE_ALISTOFACTIONS with a regular expression.&lt;br /&gt;
* The appropriate action is then done (creation in this case) by calling $customfields-&amp;gt;create($record).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At edition or creation of product lines:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Similar to &#039;&#039;&#039;At edition&#039;&#039;&#039; but with hooks: formCreateProductOptions and formEditProductOptions.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At ODT document generation and substitution:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Module&#039;s ODT generator calls for Dolibarr&#039;s function complete_substitution_array(), which then calls for CustomFields substitution file /core/substitution/functions_customfields.lib.php, function customfields_completesubstitutionarray()&lt;br /&gt;
* CustomFields&#039;s function customfields_completesubstitutionarray() calls the simplifier library customfields_aux.lib.php, function customfields_fill_object() and make the substitution inside the $substitutionarray.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At ODT document generation and substitution for product lines:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Same as the previous point but this function is called for every product lines, and thus the data is cached. Also the simplifier function is here called customfields_fill_object_lines(), and the CustomFields&#039;s substitution function called customfields_completesubstitutionarray_lines().&lt;br /&gt;
&lt;br /&gt;
= How to add the support of a new module =&lt;br /&gt;
&lt;br /&gt;
There are two cases: either the module already implements hooks, and you just have to configure CustomFields to support it, or either the module does not implements hooks nor triggers.&lt;br /&gt;
&lt;br /&gt;
This chapter will guide you through the steps to make any module supported by CustomFields (be it a Dolibarr core module or your own module).&lt;br /&gt;
&lt;br /&gt;
== Preliminary test ==&lt;br /&gt;
&lt;br /&gt;
This preliminary test will allow you to know at a glance how much work needs to be done in order to make your module supported by CustomFields, and it will also give you all the config parameters if they are available.&lt;br /&gt;
&lt;br /&gt;
Important note: this test will only give data about hooks, which is the main part to make CustomFields work for your module. But for triggers, there is no way to test, so you will have to check the triggers manually (either by testing if the custom fields are saved at creation page, or by looking at the source code of the objects classes).&lt;br /&gt;
&lt;br /&gt;
Edit the php script of the module (check the URL when you access the module&#039;s datasheet or creation page - attention both are two different pages, so you have to do this twice), then put this at the very &#039;&#039;&#039;end&#039;&#039;&#039; of the php script:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// CustomFields debuglines to show config parameters for current module&lt;br /&gt;
print(&amp;quot;&amp;lt;pre&amp;gt;&amp;quot;); // html nice output&lt;br /&gt;
print(&amp;quot;table_element: {$object-&amp;gt;table_element} - table_element_line: {$object-&amp;gt;table_element_line} - contexts: &amp;quot;);&lt;br /&gt;
print_r($hookmanager-&amp;gt;contextarray);&lt;br /&gt;
print(&amp;quot; - conf hooks_modules: &amp;quot;);&lt;br /&gt;
print_r($conf-&amp;gt;hooks_modules);&lt;br /&gt;
print(&amp;quot; - hooks attached: &amp;quot;);&lt;br /&gt;
print_r($hookmanager-&amp;gt;hooks);&lt;br /&gt;
print(&amp;quot; - user rights: &amp;quot;);&lt;br /&gt;
print_r($user-&amp;gt;rights);&lt;br /&gt;
print(&amp;quot;&amp;lt;/pre&amp;gt;&amp;quot;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now reload the page, and scroll at the very bottom. You should see some informations about the module.&lt;br /&gt;
&lt;br /&gt;
Depending on the data available, you will know what you have to do:&lt;br /&gt;
&lt;br /&gt;
* If all the datas is available, then it&#039;s ok, jump to [[Module_CustomFields_Dev#Configuring_CustomFields_to_support_the_module]] and copy these datas to the parameters of conf_customfields.lib.php&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;table_element&#039;&#039;&#039;: $object is not defined, try to find how is called the main object variable and replace $object by this variable in the debug code given above (eg: in Agenda, the object variable is called $agenda and $agendacomm).&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;table_element_line&#039;&#039;&#039;: either there&#039;s no products lines for this module, either the main object variable is not called $object (see the previous point). One last case is that the object is not standard and does not store table_element_line (the name of the table that stores its products lines), in which case you have to manually add it in the class (eg: $this-&amp;gt;table_element_line = &#039;something&#039;).&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;contexts&#039;&#039;&#039;: the hookmanager is not defined, and probably there&#039;s no hook at all in this module, so you&#039;ll have to implement them by yourself. Jump to [[Module_CustomFields_Dev#Hooks]].&lt;br /&gt;
&lt;br /&gt;
* If there is a list of &#039;&#039;&#039;contexts&#039;&#039;&#039; but the module cannot be found in the list: the hookmanager is defined but the module has not defined a context, you still have to implement the hooks. Jump to [[Module_CustomFields_Dev#Hooks]].&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;conf hooks_modules&#039;&#039;&#039;: this variable is not necessary, empty or not it doesn&#039;t matter. But if you see customfields and/or customfieldspdftest inside the list, it is a confirmation that CustomFields indeed is hooking the module correctly.&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;hooks attached&#039;&#039;&#039;, or CustomFields cannot be found in the list: CustomFields has not been properly configured to hook to this module, or you forgot to disable then re enable CustomFields in order to update the CustomFields&#039;s hooks. Jump to [[Module_CustomFields_Dev#Configuring_CustomFields_to_support_the_module]].&lt;br /&gt;
&lt;br /&gt;
* If there is no &#039;&#039;&#039;$user-&amp;gt;rights&#039;&#039;&#039;: there is either no standard security implemented in this module, or either the variable is called with another name. If there is no security, you don&#039;t need to configure it in CustomFields, but if there is a security, you need to find how the variable is called (like $object can get another name sometimes).&lt;br /&gt;
&lt;br /&gt;
* If there is &#039;&#039;&#039;$user-&amp;gt;rights&#039;&#039;&#039; but the module can&#039;t be found in the list: either the name for the rights is different (this often happens, the rights array is not yet standardized), or either the rights are hardcoded into the module&#039;s code (rights should be announced to the $user-&amp;gt;rights array, but sometimes it happens that the dev forgot to do so, and thus the right is entirely hardcoded and you can&#039;t find it in the dynamic rights array). In any of these two cases, you will have to manually read the module&#039;s code to find the right. Also, maybe there is no security implemented, but since $user-&amp;gt;rights is declared this is less probable.&lt;br /&gt;
&lt;br /&gt;
Here is an example of output on a correctly hooked module (invoice):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
table_element: facture - table_element_line: facturedet - contexts: Array&lt;br /&gt;
(&lt;br /&gt;
    [0] =&amp;gt; searchform&lt;br /&gt;
    [1] =&amp;gt; leftblock&lt;br /&gt;
    [2] =&amp;gt; toprightmenu&lt;br /&gt;
    [3] =&amp;gt; invoicecard&lt;br /&gt;
)&lt;br /&gt;
 - conf hooks_modules: Array&lt;br /&gt;
(&lt;br /&gt;
    [customfields] =&amp;gt; invoicecard:propalcard:productcard:ordercard:thirdpartycard:contactcard:ordersuppliercard:invoicesuppliercard:membercard:actioncard:projectcard:projecttaskcard:contractcard:interventioncard:doncard:tripsandexpensescard:taxvatcard:expeditioncard:invoicecard:propalcard:ordercard:ordersuppliercard:invoicesuppliercard&lt;br /&gt;
    [customfieldspdftest] =&amp;gt; Array&lt;br /&gt;
        (&lt;br /&gt;
            [0] =&amp;gt; pdfgeneration&lt;br /&gt;
            [1] =&amp;gt; ordersuppliercard&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
)&lt;br /&gt;
 - hooks attached: Array&lt;br /&gt;
(&lt;br /&gt;
    [invoicecard] =&amp;gt; Array&lt;br /&gt;
        (&lt;br /&gt;
            [customfields] =&amp;gt; ActionsCustomFields Object&lt;br /&gt;
                (&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
)&lt;br /&gt;
 - user rights: stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [user] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [user] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [lire] =&amp;gt; 1&lt;br /&gt;
                    [creer] =&amp;gt; 1&lt;br /&gt;
                    [password] =&amp;gt; 1&lt;br /&gt;
                    [supprimer] =&amp;gt; 1&lt;br /&gt;
                    [export] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [self] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [creer] =&amp;gt; 1&lt;br /&gt;
                    [password] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [user_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [readperms] =&amp;gt; 1&lt;br /&gt;
                    [write] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [self_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [readperms] =&amp;gt; 1&lt;br /&gt;
                    [writeperms] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [group_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [read] =&amp;gt; 1&lt;br /&gt;
                    [readperms] =&amp;gt; 1&lt;br /&gt;
                    [write] =&amp;gt; 1&lt;br /&gt;
                    [delete] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    [facture] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [lire] =&amp;gt; 1&lt;br /&gt;
            [creer] =&amp;gt; 1&lt;br /&gt;
            [invoice_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [unvalidate] =&amp;gt; 1&lt;br /&gt;
                    [send] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [valider] =&amp;gt; 1&lt;br /&gt;
            [paiement] =&amp;gt; 1&lt;br /&gt;
            [supprimer] =&amp;gt; 1&lt;br /&gt;
            [facture] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [export] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    [propale] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [lire] =&amp;gt; 1&lt;br /&gt;
            [creer] =&amp;gt; 1&lt;br /&gt;
            [valider] =&amp;gt; 1&lt;br /&gt;
            [propal_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [send] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [cloturer] =&amp;gt; 1&lt;br /&gt;
            [supprimer] =&amp;gt; 1&lt;br /&gt;
            [export] =&amp;gt; 1&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    [produit] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [lire] =&amp;gt; 1&lt;br /&gt;
            [creer] =&amp;gt; 1&lt;br /&gt;
            [supprimer] =&amp;gt; 1&lt;br /&gt;
            [export] =&amp;gt; 1&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    etc... a lot of other rights...&lt;br /&gt;
&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When you are finished with implementing the support of CustomFields in the module, you should delete these debug lines.&lt;br /&gt;
&lt;br /&gt;
== Implementing the support of CustomFields in the module ==&lt;br /&gt;
&lt;br /&gt;
If there is a module you want to implement the support of CustomFields, but does not yet has the required components, here is a guide on how to implement these components.&lt;br /&gt;
&lt;br /&gt;
=== Hooks ===&lt;br /&gt;
&lt;br /&gt;
Implementing hooks will allow custom fields to appear on your module&#039;s page. They will also be editable and they will save the data. For creation, you need to use a trigger (see the subchapter below).&lt;br /&gt;
&lt;br /&gt;
First you should be familiar with the way that hooks are implemented in modules. If you don&#039;t, please first [[Hooks_system#Implementation|read the hooks documentation]].&lt;br /&gt;
&lt;br /&gt;
An example case of implementation of hooks into a Dolibarr core file can be found in the [[Module_CustomFields_Cases|CustomFields Example Cases wiki page]].&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Datasheet hook&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
To implement CustomFields in your module, you must initialize the hook manager, then use the following hook context:&lt;br /&gt;
* formObjectOptions&lt;br /&gt;
&lt;br /&gt;
This hook must be placed everywhere in your code where you want custom fields to appear (eg: on the creation page and datasheet view page).&lt;br /&gt;
&lt;br /&gt;
Note: Generally, you will have to place the hook just before the end of a table tag (&#039;&#039;&#039;&amp;lt;/table&amp;gt;&#039;&#039;&#039;), so if you don&#039;t know where to place it (eg: if you&#039;re implementing CustomFields on a Dolibarr core module), just search for this tag in the page shown in the url (eg: /htdocs/commande/fiche.php).&lt;br /&gt;
&lt;br /&gt;
Eg: First load the hookmanager at the beginning of your php script:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Initialize technical object to manage hooks of thirdparties. Note that conf-&amp;gt;hooks_modules contains array array&lt;br /&gt;
include_once(DOL_DOCUMENT_ROOT.&#039;/core/class/hookmanager.class.php&#039;);&lt;br /&gt;
$hookmanager=new HookManager($db);&lt;br /&gt;
$hookmanager-&amp;gt;initHooks(array(&#039;yourmodule&#039;));&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then place the hook as many times as required:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// ... a lot of php code ...&lt;br /&gt;
&lt;br /&gt;
print &#039;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&#039;; // end of the last row of a table&lt;br /&gt;
&lt;br /&gt;
// Other attributes&lt;br /&gt;
$parameters=array(&#039;colspan&#039; =&amp;gt; &#039; colspan=&amp;quot;3&amp;quot;&#039;); // specifying the colspan is optional but will help CustomFields know how to correctly print the table&lt;br /&gt;
$reshook=$hookmanager-&amp;gt;executeHooks(&#039;formObjectOptions&#039;,$parameters,$object,$action);    // Note that $action and $object may have been modified by hook&lt;br /&gt;
&lt;br /&gt;
print &#039;&amp;lt;/table&amp;gt;&#039;; // end of the table, this is what you&#039;re looking for, place the hook just above&lt;br /&gt;
&lt;br /&gt;
// ... a lot of php code ... redo the same if you find another &amp;lt;/table&amp;gt; and there&#039;s a page where there&#039;s no custom fields but you want to place custom fields on there&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note2: If after implementing the hook you receive an error or custom fields don&#039;t appear, you may have to change $object to another variable, depending on what is called the main object (eg: in Agenda it&#039;s called $action and $actioncomm).&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Lines hooks&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
For product lines, you should anyway use the product lines template files: htdocs/core/tpl/freeproductline_edit.tpl.php&lt;br /&gt;
&lt;br /&gt;
If you don&#039;t, you can still manually implement the required hooks, what is required is that you supply $line (which is the current line object being processed) in the $parameters array:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$parameters=array(&#039;line&#039;=&amp;gt;$line,&#039;fk_parent_line&#039;=&amp;gt;$line-&amp;gt;fk_parent_line);&lt;br /&gt;
&lt;br /&gt;
echo $hookmanager-&amp;gt;executeHooks(&#039;formEditProductOptions&#039;,$parameters,$this,$action);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hooks names:&lt;br /&gt;
* formEditProductOptions&lt;br /&gt;
* formCreateProductOptions ($line is not required here, because we create a new product line! you can leave $parameters=array(); empty).&lt;br /&gt;
Note: there&#039;s no hook for viewing, because showing the customfields will clutter the visual field, but if you really want you can also do it by adding a formViewProductOptions hook.&lt;br /&gt;
&lt;br /&gt;
=== Triggers ===&lt;br /&gt;
&lt;br /&gt;
First you need to know how to implement triggers in Dolibarr. For more informations about how to implement triggers in your module, please read the [[Triggers#Manage_and_create_a_new_trigger.27s_action|triggers wiki page]].&lt;br /&gt;
&lt;br /&gt;
An example case of implementation of hooks into a Dolibarr core file can be found in the [[Module_CustomFields_Cases|CustomFields Example Cases wiki page]].&lt;br /&gt;
&lt;br /&gt;
Implementing triggers on your module will allow CustomFields to save the fields at creation (datasheet edition is already managed by hooks).&lt;br /&gt;
&lt;br /&gt;
You need to implement 3 triggers:&lt;br /&gt;
- on creation of object - eg: YOURMODULE_CREATE&lt;br /&gt;
- on creation of lines - eg: YOURMODULELINE_INSERT (optional, only if your module has product lines)&lt;br /&gt;
- on update of lines - eg: YOURMODULELINE_UPDATE (optional, only if your module has product lines)&lt;br /&gt;
&lt;br /&gt;
Note: you need to supply the sql id of the newly created object before calling the trigger.&lt;br /&gt;
&lt;br /&gt;
Eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$this-&amp;gt;id = $this-&amp;gt;db-&amp;gt;last_insert_id(MAIN_DB_PREFIX.&amp;quot;yourmodule_table_element&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
// Appel des triggers&lt;br /&gt;
include_once(DOL_DOCUMENT_ROOT . &amp;quot;/core/class/interfaces.class.php&amp;quot;);&lt;br /&gt;
$interface=new Interfaces($this-&amp;gt;db);&lt;br /&gt;
$result=$interface-&amp;gt;run_triggers(&#039;YOURMODULE_CREATE&#039;,$this,$user,$langs,$conf);&lt;br /&gt;
if ($result &amp;lt; 0) {&lt;br /&gt;
    $error++; $this-&amp;gt;errors=$interface-&amp;gt;errors;&lt;br /&gt;
}&lt;br /&gt;
// Fin appel triggers&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note2: if it&#039;s a Dolibarr core module, you can easily find the right place to put the trigger by searching for &#039;create sql&#039; in the class subfolder (eg: URL is /htdocs/commande/fiche.php, the class is in /htdocs/commande/class/commande.class.php).&lt;br /&gt;
&lt;br /&gt;
Note3: CustomFields also supports a few other triggers like _CLONE, _UPDATE, _MODIFY, but they are not required and may not work depending on how your module works to manage these actions.&lt;br /&gt;
&lt;br /&gt;
== Configuring CustomFields to support the module ==&lt;br /&gt;
&lt;br /&gt;
=== Preliminary work ===&lt;br /&gt;
&lt;br /&gt;
You need to get the parameters of this module, and you will then put them in the CustomFields configuration file (conf_customfields.lib.php).&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Getting the parameters automatically&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
You can use the [[Module_CustomFields_Dev#Preliminary_test|preliminary test]] to easily get all those parameters.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Getting the parameters manually&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
To configure CustomFields to support a module, you need at least 5 parameters:&lt;br /&gt;
* the module&#039;s table_name (name of the sql table where the module stores its datas)&lt;br /&gt;
* the module&#039;s context (name given by the module to the hookmanager)&lt;br /&gt;
* the module&#039;s idvar (name of the variable that holds the sql row id of the record)&lt;br /&gt;
* the module&#039;s trigger action(s) (triggers on creation and update, see the related chapter above)&lt;br /&gt;
* the module&#039;s rights (for security purposes)&lt;br /&gt;
&lt;br /&gt;
To get these parameters manually, you can take a look inside the code of the module&#039;s files you want to implement:&lt;br /&gt;
* module&#039;s table name: either find it by yourself in the database (looks like llx_themodulename) or by printing it in actions_customfields.class.php by adding &#039;&#039;&#039;print_r($parameters)&#039;&#039;&#039; and search for $parameters-&amp;gt;table_element&lt;br /&gt;
* module&#039;s context: search for &amp;quot;callHooks(&amp;quot; without the quotes or take a look at the wiki: http://wiki.dolibarr.org/index.php/Hooks_system&lt;br /&gt;
or you can get it by printing it in the /htdocs/customfields/class/actions_customfields.class.php by adding print_r($parameters) and search for $parameters-&amp;gt;context&lt;br /&gt;
* module&#039;s trigger actions: search for &amp;quot;run_triggers(&amp;quot; or take a look at the wiki: http://wiki.dolibarr.org/index.php/Triggers#List_of_known_triggers_actions&lt;br /&gt;
* module&#039;s idvar: look at the url when you access one of the module&#039;s record, and you should see something like fiche.php?xxxid=12 - you need the xxxid (eg: socid, facid, etc.).&lt;br /&gt;
* module&#039;s security: in the page shown in URL when accessing the module&#039;s datasheet, edit this file and add &#039;&#039;&#039;print($user-&amp;gt;rights)&#039;&#039;&#039;, then in the list try to find the correct right (generally something like $user-&amp;gt;modulename-&amp;gt;creer). You can also try to search for $user-&amp;gt;rights in the sourcecode of this module.&lt;br /&gt;
&lt;br /&gt;
With these values, edit the config file (/htdocs/customfields/conf/conf_customfields.lib.php), particularly the $modulesarray and $triggersarray variables:&lt;br /&gt;
&lt;br /&gt;
=== Configure hooks and main parameters ===&lt;br /&gt;
&lt;br /&gt;
Add every parameters except triggers in the $modulesarray:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$modulesarray = array(&lt;br /&gt;
                      array(&#039;context&#039;=&amp;gt;&#039;invoicecard&#039;, &#039;table_element&#039;=&amp;gt;&#039;facture&#039;, &#039;idvar&#039;=&amp;gt;&#039;facid&#039;, &#039;rights&#039;=&amp;gt;array(&#039;$user-&amp;gt;rights-&amp;gt;facture-&amp;gt;creer&#039;)), // Client Invoice&lt;br /&gt;
                      array(&#039;context&#039;=&amp;gt;&#039;membercard&#039;, &#039;table_element&#039;=&amp;gt;&#039;adherent&#039;, &#039;idvar&#039;=&amp;gt;&#039;rowid&#039;, &#039;rights&#039;=&amp;gt;array(&#039;$user-&amp;gt;rights-&amp;gt;adherent-&amp;gt;creer&#039;), &#039;tabs&#039;=&amp;gt;array(&#039;objecttype&#039;=&amp;gt;&#039;member_admin&#039;, &#039;function&#039;=&amp;gt;&#039;member_admin_prepare_head&#039;, &#039;lib&#039;=&amp;gt;DOL_DOCUMENT_ROOT.&#039;/core/lib/member.lib.php&#039;)), // Members / Adherents&lt;br /&gt;
                     ); // Edit me to add the support of another module - NOTE: Lowercase only!&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are also a lot of others parameters you can give to CustomFields so that the module gets better managed or to add a few other functionnalities, here is a non-exhaustive list:&lt;br /&gt;
* context: name of the module&#039;s context.&lt;br /&gt;
* table_element: name of the sql table that holds the object&#039;s datas.&lt;br /&gt;
* idvar: name of the variable that holds the id of the object in the sql database (by default &#039;id&#039; or &#039;rowid&#039;).&lt;br /&gt;
* rights: prevent the custom fields to be editable unless all the rights set here are enabled (preventing custom fields to be viewed is useless because they won&#039;t show anyway if the page can&#039;t be loaded due to insufficient rights, so this is directly handled by the module).&lt;br /&gt;
* tabs=&amp;gt;array(objecttype, function, lib, tabname, tabtitle): used to embed CustomFields&#039;s admin panel into another module&#039;s admin page.&lt;br /&gt;
&lt;br /&gt;
=== Configure creation triggers ===&lt;br /&gt;
&lt;br /&gt;
Add the triggers in $triggersarray:&lt;br /&gt;
$triggersarray = array(&amp;quot;order_create&amp;quot;=&amp;gt;&amp;quot;commande&amp;quot;,&lt;br /&gt;
                       &amp;quot;yourmodule_triggeraction1&amp;quot;=&amp;gt;&amp;quot;yourmodule_tablename&amp;quot;,&lt;br /&gt;
                       &amp;quot;yourmodule_triggeraction2&amp;quot;=&amp;gt;&amp;quot;yourmodule_tablename&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
Note: generally you will be looking to implement the _CREATE.&lt;br /&gt;
&lt;br /&gt;
Note2: if a triggeraction is not recognized by CustomFields (eg: mymodule_addpayment) then by default CustomFields will assimilate it to a _create action (it will create and save the values for the custom fields, just for a module&#039;s creation page).&lt;br /&gt;
&lt;br /&gt;
Note3: CustomFields also supports a few other triggers like _CLONE, _UPDATE, _MODIFY, but they are not required and may not work depending on how your module works to manage these actions.&lt;br /&gt;
&lt;br /&gt;
=== Configuration troubleshooting ===&lt;br /&gt;
&lt;br /&gt;
* If you cannot see the custom fields appear on your module&#039;s page, this means the hooks are not implemented or configured in CustomFields, please read the related chapters.&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet but there&#039;s no edit button icon: the module implements security but you did not configure it properly in CustomFields, please find the right security in $user-&amp;gt;rights and set it in conf_customfields.lib.php.&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the creation page but are not saved: you forgot to set the trigger in $triggersarray, or the creation trigger does not exist for this module and you have to implement it. Jump to [[Module_CustomFields_Dev#Triggers]].&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet but not on the creation page (or vice versa): A hook is missing on the page where the custom fields do not appear. Jump to [[Module_CustomFields_Dev#Hooks]].&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet with the edit button, but when the edit button is clicked, everything disappear: either the $idvar is not properly set (look at the URL to know if you need to set idvar), or the hook is not properly made ($object is not defined and it&#039;s called by another name, but the hook use $object instead of the correct variable).&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet with the edit button, can be edited but cannot be saved (the data is not saved): same as the previous point: either $idvar or $object bug.&lt;br /&gt;
&lt;br /&gt;
Also you might find useful to &#039;&#039;&#039;[[Module_CustomFields#General_advices_for_troubleshooting|enable php errors]]&#039;&#039;&#039; to better track errors if that&#039;s not already done.&lt;br /&gt;
&lt;br /&gt;
= Example cases =&lt;br /&gt;
&lt;br /&gt;
You can find a compilation of concrete, practical examples on the [[Module_CustomFields_Cases]] wiki page.&lt;br /&gt;
&lt;br /&gt;
= TO DO =&lt;br /&gt;
&lt;br /&gt;
You will find below the public list of features that should be implemented in the future.&lt;br /&gt;
&lt;br /&gt;
If you have an idea about a new feature that isn&#039;t listed below, feel free to add it to the list!&lt;br /&gt;
&lt;br /&gt;
== To Do ==&lt;br /&gt;
&lt;br /&gt;
* Better management of hidden fields with custom cascading (eg: works great on creation form, but on edit form and creation form with edit action, if the parents already have a value, the hidden fields will stay hidden! This is because of the AJAX is not called, and this is normal). -&amp;gt; Propose a better custom cascade function storing the hidden field&#039;s state inside extraoptions?&lt;br /&gt;
&lt;br /&gt;
== To Document ==&lt;br /&gt;
* Automatic recopy on cloning for custom fields and products&#039; lines custom fields.&lt;br /&gt;
* new API function: customfields_clone_or_recopy($object, $fromobject, $action2 = null)&lt;br /&gt;
&lt;br /&gt;
== Should do ==&lt;br /&gt;
* Add the possibility to mark a custom field to be shown in tables listings (eg: as a column in products listings) with &amp;quot;order by this column&amp;quot; buttons and stuffs. Will be difficult if the hooks are not yet implemented in listings.&lt;br /&gt;
* Checkbox (multiple checkboxes, one for each possible value) and multiselect (big select and can use SHIFT to multiselect) (store in XML file) (but impossible with constrained fields, can&#039;t store multiple foreign keys ids in one single sql field, but would work for other types like enum)&lt;br /&gt;
use fieldset, input checkbox and label for, eg:&lt;br /&gt;
    &amp;lt;fieldset&amp;gt;&amp;lt;legend&amp;gt;&amp;lt;label for=&amp;quot;cf_productslist&amp;quot;&amp;gt; $langs-&amp;gt;trans(&#039;cf_productslist&#039;)&amp;lt;/label&amp;gt;&amp;lt;/legend&amp;gt;&lt;br /&gt;
    &amp;lt;input type=&amp;quot;checkbox&amp;quot; name=&amp;quot;cf_productslist_choice1&amp;quot; value=&amp;quot;1&amp;quot; id=&amp;quot;cf_productslist_choice1&amp;quot; /&amp;gt;&amp;lt;label for=&amp;quot;cf_productslist_choice1&amp;quot;&amp;gt; $langs-&amp;gt;trans(&#039;cf_productslist_choice1&#039;)&amp;lt;/label&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
    &amp;lt;input type=&amp;quot;checkbox&amp;quot; name=&amp;quot;cf_productslist_choice2&amp;quot; value=&amp;quot;1&amp;quot; id=&amp;quot;cf_productslist_choice2&amp;quot; /&amp;gt;&amp;lt;label for=&amp;quot;cf_productslist_choice2&amp;quot;&amp;gt; $langs-&amp;gt;trans(&#039;cf_productslist_choice2&#039;)&amp;lt;/label&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
    etc...&lt;br /&gt;
    &amp;lt;/fieldset&amp;gt;&lt;br /&gt;
* Add a javascript options values generator for the enum type (a hidden input that would be shown only when DropdownBox type is selected and that would permit to add options by clicking a plus button).&lt;br /&gt;
* Replace triggers in customfields class by hookmanager?&lt;br /&gt;
* AJAX previsualisation of the HTML custom field that will be created.&lt;br /&gt;
* In ODTs, create special tags to directly access day, month or year value of a date type field independently of the rest.&lt;br /&gt;
* New overloading function&#039;s action: &amp;quot;get&amp;quot;, similar to view except that it works not only on Dolibarr&#039;s view but whenever someone tries to access this field&#039;s value.&lt;br /&gt;
* Add Upload and Image field types (this would allow for an easier integration inside ODT and PDF templates than the current workflow of Dolibarr).&lt;br /&gt;
&lt;br /&gt;
== Known bugs ==&lt;br /&gt;
* in product and service modules, if you edit a field, the proposals and other fields below won&#039;t be shown, you need to refresh the page. This problem resides in Dolibarr I think (since we are simply using a hook).&lt;br /&gt;
* ficheinter (Intervention module) custom fields will stay in create mode just after create page is submitted (this is because of the $action that is not standard - $action=edit or something).&lt;br /&gt;
&lt;br /&gt;
== Never/Maybe one day ==&lt;br /&gt;
* Add support for repeatable (predefined) invoices (the way it is currently managed makes it very difficult to manage this without making a big exception, adding specific functions in customfields modules that would not at all will be reusable anywhere else, when customfields has been designed to be as generic as possible to support any module and any version of dolibarr, because it&#039;s managed by a totally different table while it&#039;s still managed by the same module, CustomFields work with the paradigm: one module, one table).&lt;br /&gt;
* Add an AJAX select box for constrained values : when a constrained type is selected and a table is selected, a hidden select box would show up with the list of the fields of this table to choose the values that will be printed as the values for this customfield (eg: for table llx_users you could select the &amp;quot;nom&amp;quot; field and then it would automatically prepend &amp;quot;nom_&amp;quot; to the field&#039;s name).&lt;br /&gt;
* Fine-grained rights management: rights per group, per user, and per field.&lt;br /&gt;
* Replace overloading functions (extend class) by hookmanager if possible (but how to simulate the *full switch? Plus this will force users to make their own modules to make those functions, and to disable/renable their modules everytime they will add a new function...).&lt;br /&gt;
* Refactor the trigger array: merge it with the modulesarray (along with a new way to specify which customfields trigger action a trigger should correspond, eg: &#039;linebill_insert&#039;=&amp;gt;&#039;customfields_create&#039;). But the triggers aren&#039;t necessarily associated with a specific module...&lt;/div&gt;</summary>
		<author><name>Lrq3000</name></author>
	</entry>
	<entry>
		<id>https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Dev&amp;diff=33143</id>
		<title>Module CustomFields Dev</title>
		<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Dev&amp;diff=33143"/>
		<updated>2015-02-11T18:17:15Z</updated>

		<summary type="html">&lt;p&gt;Lrq3000: /* Should do */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:CustomFields]]&lt;br /&gt;
{{TemplateDocDevEn}}&lt;br /&gt;
{{TemplateModEN}}&lt;br /&gt;
{{BasculeDevUserEn|&lt;br /&gt;
name=CustomFields|&lt;br /&gt;
num=8500|&lt;br /&gt;
devdoc=This page|&lt;br /&gt;
userdoc=[[Module_CustomFields]]|}}&lt;br /&gt;
[[Category:FAQ EN]]&lt;br /&gt;
&lt;br /&gt;
These notes are aimed to developpers who want to extend the functionnalities of the module or want to use it for extreme cases.&lt;br /&gt;
This is not meant to guide implementers on how to implement and configure the module (see the related chapter for that).&lt;br /&gt;
&lt;br /&gt;
If you are an implementer and just want to use CustomFields with its normal functions, you can read the [[Module_CustomFields|user documentation of CustomFields]].&lt;br /&gt;
&lt;br /&gt;
You can also jump directly to [[Module_CustomFields_Cases]] wiki page for a collection of practical examples.&lt;br /&gt;
&lt;br /&gt;
= Principles =&lt;br /&gt;
&lt;br /&gt;
CustomFields is simply a wrapper for SQL databases.&lt;br /&gt;
&lt;br /&gt;
The module is based on SQL to manage the fields and most of the stuff. It uses and understands standard SQL.&lt;br /&gt;
&lt;br /&gt;
The main principles of CustomFields are:&lt;br /&gt;
&lt;br /&gt;
* one SQL column = one Dolibarr field.&lt;br /&gt;
* one SQL type = one method to show and edit the field in Dolibarr.&lt;br /&gt;
* almost everything should be configurable from the configuration file, or admin interface.&lt;br /&gt;
* be independent from Dolibarr&#039;s data structures.&lt;br /&gt;
&lt;br /&gt;
CustomFields sits besides Dolibarr, and does not mix with it, even if in the Dolibarr&#039;s interface it looks like CustomFields is embedded inside Dolibarr, in reality, CustomFields is totally independent.&lt;br /&gt;
&lt;br /&gt;
= Goals =&lt;br /&gt;
&lt;br /&gt;
This module was made to provide an easy way to manage custom sql fields, not only for users and implementers, but also for developpers to use in their own module:&lt;br /&gt;
* by easily adding the support of custom fields for your own module by using this module;&lt;br /&gt;
* by reusing custom fields from another module in your own module;&lt;br /&gt;
* by managing sql fields in your own module by using the CustomFields class (which will take care of everything from printing, editing to PDF/ODT templating).&lt;br /&gt;
&lt;br /&gt;
So this module is not only an auxiliary of other modules, but it can also be the core part of your own module by using it as your SQL query class for fields.&lt;br /&gt;
&lt;br /&gt;
= SQL structure =&lt;br /&gt;
&lt;br /&gt;
A CustomFields table needs to respect three parameters that never change:&lt;br /&gt;
&lt;br /&gt;
* table&#039;s name (format: llx_*dolibarrmodule*_customfields where *dolibarrmodule* = table_element in conf_customfields.lib.php)&lt;br /&gt;
* first column: rowid, which is the primary key for each field&lt;br /&gt;
* second column: fk_*dolibarrmodule* which is the foreign key that links every customfields record to the Dolibarr&#039;s module that is associated (invoice, order, etc...).&lt;br /&gt;
&lt;br /&gt;
Apart from the table&#039;s name and the first two special columns, every other columns are just standard sql columns that you can freely create, edit or delete as you want, from the module&#039;s administration page or from a third-party SQL management tool (such as phpMyAdmin).&lt;br /&gt;
&lt;br /&gt;
You can also create more complex sql fields: constraints, foreign keys, transformations, other sql types, etc...).&lt;br /&gt;
&lt;br /&gt;
Then the module will try to manage the sql fields you manually created the best it can:&lt;br /&gt;
* by printing appropriate input fields in the Dolibarr gui interface.&lt;br /&gt;
* by providing the developper an easy access to these fields in php (eg: $customfields-&amp;gt;cf_myfield or $object-&amp;gt;customfields-&amp;gt;cf_myfield) for any use, including PDF templates and ODT templates, or in any php script (so that you can use these fields in your own module).&lt;br /&gt;
&lt;br /&gt;
= SQL Indexes =&lt;br /&gt;
&lt;br /&gt;
The module automatically adds an index on the primary key and foreign key (first and second columns).&lt;br /&gt;
&lt;br /&gt;
To further optimize the performances of your database, and particularly the customfields tables, you may want to manually add indexes based on usage.&lt;br /&gt;
&lt;br /&gt;
Also, you may add indexes on remote tables used by constrained fields, which could optimize even more the performances since constrained fields have the worst performance drawback for CustomFields.&lt;br /&gt;
&lt;br /&gt;
= SQL Compatibility mode =&lt;br /&gt;
&lt;br /&gt;
The module relies on referential integrity checks and foreign keys of your DBMS to keep the database clean and to provide the constrained fields feature. Referential integrity checks is part of the SQL standards and should be used whenever possible, which always was a main goal for CustomFields (to be respectful of SQL standards).&lt;br /&gt;
&lt;br /&gt;
Unluckily, a few DBMS systems still don&#039;t support this critical functionality of the SQL standard, for example MySQL enables MyISAM engine by default, which doesn&#039;t support this feature.&lt;br /&gt;
&lt;br /&gt;
In these cases, you can either do two things:&lt;br /&gt;
* Switch to an engine which support referential integrity. For example with MySQL, you can enable InnoDB, which support referential integrity checks and foreign keys.&lt;br /&gt;
* Use CustomFields compatibility mode (more infos below).&lt;br /&gt;
&lt;br /&gt;
In the case your database does not support referential integrity checks, CustomFields will automatically switch to the SQL compatibility mode, which will allow you to use CustomFields even on databases where foreign keys are not possible.&lt;br /&gt;
&lt;br /&gt;
In compatibility mode, you will get nearly all CustomFields advanced features such as Constrained fields, but the customfields&#039; tables in the database won&#039;t be cleaned automatically (no delete on cascade, meaning that when you delete an object, the linked custom fields will still be kept in your database), and constrained fields won&#039;t be updated automatically on update.&lt;br /&gt;
&lt;br /&gt;
Nevertheless, these missing cleanup functionalities won&#039;t be seen by your users because they will never be accessible: on deletion of an object, the custom fields won&#039;t ever be accessible again from Dolibarr&#039;s interface, and object&#039;s ids are never changed since Dolibarr also relies on this. Thus in compatibility mode, CustomFields will act just like normally, but your database will be a bit less clean.&lt;br /&gt;
&lt;br /&gt;
= JSON extension for extra options =&lt;br /&gt;
&lt;br /&gt;
== Introduction to JSON in CustomFields ==&lt;br /&gt;
The relational model is very great to store and fetch data with a declarative language, and this is very powerful, but this model also imposes a few limitations, for example the relations must be well formatted and predefined.&lt;br /&gt;
&lt;br /&gt;
That&#039;s why we finally implemented a &#039;&#039;&#039;JSON extension&#039;&#039;&#039; to manage &#039;&#039;&#039;extra options&#039;&#039;&#039; and associate them with custom fields. This allows to add many properties (or columns if you are more used to the relational model) on-the-fly without having to predefine them prior to storing and using them.&lt;br /&gt;
&lt;br /&gt;
This allows to add an undefined number of columns/properties/extra options that will help manage special behaviours for custom fields. For example, it is currently used to allow to change the order of appearance of custom fields, and to store and manage remote table and columns datas for constrained fields on DBMS that do not support Foreign Keys checks.&lt;br /&gt;
&lt;br /&gt;
We chose JSON over XML because the library are simpler and the data footprint smaller, and because CustomFields does not need advanced features of XML like XQuery, data typing and meta structures (at least not for now).&lt;br /&gt;
&lt;br /&gt;
The other advantage is that php objects can directly be encoded into JSON without any preprocessing, and can then as straightly be decoded from JSON back into a php object.&lt;br /&gt;
&lt;br /&gt;
A simple mean to encode and decode back an object into and from a storage place, as well as allow users to easily add/remove extra options were the main requirement, and JSON (+ JSON libraries for PHP) meet this criteria.&lt;br /&gt;
&lt;br /&gt;
However, note that the extra options are now encoded as an associative array, so as to avoid any ambiguities (there&#039;s no associative array in JSON, they are converted to objects).&lt;br /&gt;
&lt;br /&gt;
== Implementation of JSON ==&lt;br /&gt;
The implementation is very simple: a table called &#039;&#039;&#039;llx_customfields_extraoptions&#039;&#039;&#039; is created when you enable the CustomFields module, and this table only contains three columns:&lt;br /&gt;
* table_name (varchar)&lt;br /&gt;
* column_name (varchar)&lt;br /&gt;
* extraoptions (blob)&lt;br /&gt;
&lt;br /&gt;
(table_name, column_name) both forms the primary key of the table, and links to columns (custom fields) of other customfields tables.&lt;br /&gt;
extraoptions is a blob, which allows to store the data as binary (as-is) without encoding (which solves a big problem of JSON which is encoding special and foreign characters, here there is no problem at all).&lt;br /&gt;
&lt;br /&gt;
When fetching custom fields, the module simply JOIN the extraoptions table, which adds an &#039;&#039;&#039;extraoptions&#039;&#039;&#039; column for each custom field in the SQL result. Then it is quite easy to just json_decode() the data returned inside the extraoptions column.&lt;br /&gt;
&lt;br /&gt;
This methodology allows to have all the advantages of a relational database, with the flexibility of a free-schema model:&lt;br /&gt;
* everything is stored inside the database (only one storage place)&lt;br /&gt;
* fixed columns allows for relational joining and optimization&lt;br /&gt;
* thus queries (fetching) are optimised&lt;br /&gt;
* at the same time, the extraoptions are automatically retrieved and associated with every custom field, and whenever needed they can be used&lt;br /&gt;
* and thus we also get a flexible model to &#039;&#039;&#039;add extra (virtual) columns whenever needed&#039;&#039;&#039; without having to alter the relational database schema&lt;br /&gt;
&lt;br /&gt;
Thus no critical information is and should be stored inside the extraoptions column because they would not be query-able, only extra options that are just used to tweak the management of those fields inside the PHP application should be stored. But this is a wonderful way to add non critical informations that still allows to develop more complex features and management for special custom fields.&lt;br /&gt;
&lt;br /&gt;
== CustomFields JSON API ==&lt;br /&gt;
&lt;br /&gt;
=== Storing extra options ===&lt;br /&gt;
&lt;br /&gt;
You can use the &#039;&#039;&#039;setExtra()&#039;&#039;&#039; method to set extra options of a custom field.&lt;br /&gt;
&lt;br /&gt;
Eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$extra = array(); // $extra should always be an associative array, it&#039;s no longer an object&lt;br /&gt;
$extra[&#039;myproperty&#039;] = &#039;Anything I want here&#039;; // set a property/extra option like this&lt;br /&gt;
$extra[&#039;category&#039;][&#039;subcategory&#039;][&#039;prop&#039;] = true; // we can set recursive properties, there&#039;s not any limit to how deep the hierarchy can be&lt;br /&gt;
&lt;br /&gt;
require_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomField($db, &#039;currentmodule&#039;);&lt;br /&gt;
$fieldid = &#039;mycustomfields&#039;; // $fieldid can either be a column_name or an ordinal_position from information_schema.columns&lt;br /&gt;
$result = $customfields-&amp;gt;setExtra($fieldid, $extra); // store the extra options inside the database&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can also use addCustomField() or updateCustomField() with an $extra array, and these functions will also call the setExtra() method.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: the extra options you set are &#039;&#039;&#039;appended&#039;&#039;&#039; to the ones already stored in the database. However, you can also overwrite previous options, just use the same name of property but with a different value (the replacement is recursive, you can do it at any level in the hierarchy).&lt;br /&gt;
&lt;br /&gt;
=== Fetching extra options ===&lt;br /&gt;
&lt;br /&gt;
Extra options are automatically fetched along with a custom field structure, decoded, and stored inside the extra sub-property of any field object, or CustomFields object.&lt;br /&gt;
&lt;br /&gt;
When fetched, extra options are stored as a sub-object for the current field object (which itself is stored as a subobject of the CustomFields object if caching is enabled).&lt;br /&gt;
&lt;br /&gt;
Eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
require_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomField($db, &#039;currentmodule&#039;);&lt;br /&gt;
$fields = fetchAllFieldsStruct(); // fetching fields structure will automatically fetch extra options at the same time. This works with fetchAllFieldsStruct() and fetchFieldStruct()&lt;br /&gt;
&lt;br /&gt;
// Now that you fetched the fields structure, you can just access the extra options sub-properties:&lt;br /&gt;
print_r($fields-&amp;gt;mycustomfield-&amp;gt;extra); // will print all extra options for the field mycustomfield&lt;br /&gt;
print_r($customfields-&amp;gt;fields-&amp;gt;mycustomfield-&amp;gt;extra); // you can also access the same field(s) directly from the CustomFields object if caching is enabled (default behaviour)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Modifying extra options ===&lt;br /&gt;
&lt;br /&gt;
You can just combine the two previous points to edit extra options of a custom field: just load a custom field, edit on-the-fly the extra property with PHP code, and then store it back directly into the database:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
require_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomField($db, &#039;currentmodule&#039;);&lt;br /&gt;
$fields = fetchAllFieldsStruct();&lt;br /&gt;
&lt;br /&gt;
$fields-&amp;gt;mycustomfield-&amp;gt;extra[&#039;newproperty&#039;] = &#039;This is a new property&#039;; // add a new property over the old extra options sub-object&lt;br /&gt;
$fields-&amp;gt;mycustomfield-&amp;gt;extra[&#039;mycategory&#039;] = &#039;Overwrite the old category&#039;; // this will replace the previous value of &#039;mycategory&#039; extra option&lt;br /&gt;
&lt;br /&gt;
$customfields-&amp;gt;setExtra(&#039;mycustomfield&#039;, $fields-&amp;gt;mycustomfield-&amp;gt;extra); // store the modified extra options back into the database for future usage&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= CustomFields API =&lt;br /&gt;
&lt;br /&gt;
== Introduction to CustomFields API ==&lt;br /&gt;
&lt;br /&gt;
This chapter will cover the basics on how to manually and concretely use the CustomFields class to create/update/fetch/manage custom fields.&lt;br /&gt;
&lt;br /&gt;
This will give you a better understanding of how CustomFields works, as well as giving you insights on how to use it for your own applications.&lt;br /&gt;
&lt;br /&gt;
Important note: contrary to the Facade API, the core CustomFields class use columns names without any prefix, so don&#039;t try to use the prefix, it won&#039;t work! For example: if your field is named &#039;mycustomfield&#039; and prefix is &#039;cf_&#039;, when you use the Facade API you should use &#039;$object-&amp;gt;$path-&amp;gt;cf_mycustomfield&#039; but when you use the core CustomFields class directly, $fieldname = &#039;mycustomfield&#039;.&lt;br /&gt;
&lt;br /&gt;
== Prerequisite to use the CustomFields class ==&lt;br /&gt;
&lt;br /&gt;
Before using the CustomFields class, you first must instanciate it. This is a necessary preliminary step before using any of the methods that will be covered inside this chapter, and thus will not be repeated, only shown once here.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Include the CustomFields class (which contains the main API)&lt;br /&gt;
require_once(dirname(__FILE__).&#039;/../class/customfields.class.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Instanciate a CustomFields object&lt;br /&gt;
global $db; // the database handler created by Dolibarr&lt;br /&gt;
$table_element = &#039;currentmodule&#039;; // where $table_element is the database&#039;s table name for the module, without the Dolibarr prefix (eg: for &#039;llx_facture&#039;, table_element is &#039;facture&#039;). You can also find it inside the $object properties (just print_r() it).&lt;br /&gt;
$customfields = new CustomFields($db, $table_element); // instanciate the CustomFields class inside $customfields object&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
CustomFields keeps track of errors that can happen when executing methods and SQL queries inside the &#039;&#039;&#039;error&#039;&#039;&#039; and &#039;&#039;&#039;errors&#039;&#039;&#039; properties, which you can print with the printErrors() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Do something with CustomFields before&lt;br /&gt;
&lt;br /&gt;
if (count($customfields-&amp;gt;errors) &amp;gt; 0) { // If there is at least one error&lt;br /&gt;
    $customfields-&amp;gt;printErrors(); // print errors&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// You can also print your own error message, in two manners:&lt;br /&gt;
&lt;br /&gt;
// 1- submit your own message&lt;br /&gt;
$customfields-&amp;gt;printErrors(&#039;Your own error message here&#039;); // this will print ONLY your error message, using the Dolibarr&#039;s AJAX error dialog (standard way of printing errors)&lt;br /&gt;
&lt;br /&gt;
// 2- append your message in the customfields errors&lt;br /&gt;
$customfields-&amp;gt;errors[] = &#039;Your own error message to be appended with other CustomFields messages&#039;;&lt;br /&gt;
$customfields-&amp;gt;printErrors(); // Here your error message will be appended, thus all error messages will be printed. This allows you for example to add more intelligible informations on a specific error.&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== General purpose SQL methods ==&lt;br /&gt;
&lt;br /&gt;
CustomFields provide an interface to easily execute various common SQL requests, as well as your own custom SQL requests.&lt;br /&gt;
&lt;br /&gt;
* fetchAllTables() fetch a list of all the tables in the database&lt;br /&gt;
* fetchPrimaryField($table) fetch the column_name (and only the column_name!) of the primary field of a table. You can then use fetchFieldStruct()  with the column_name to get any information you want.&lt;br /&gt;
* fetchAny($columns, $table, $where=&#039;&#039;, $orderby=&#039;&#039;, $limitby=&#039;&#039;) allows you to issue any simple SQL query command, for example fetchAny(&#039;*&#039;, &#039;sometable&#039;) is equivalent to SELECT * FROM sometable; (but then everything is managed automatically and result is returned to you)&lt;br /&gt;
* executeSQL($sql, $eventname) to execute any SQL query you want, this will return you a resource (and NOT a processed nuplet like fetchAny!). $eventname can be anything, it&#039;s just for Dolibarr logging facility.&lt;br /&gt;
* executeMultiSQL($sql, $eventname) to execute multiple SQL queries&lt;br /&gt;
* fetchFieldStruct($id=null, $nohide=false, $table=null, $cachepath=null) allows you to fetch the field&#039;s SQL structure of not only custom fields, but of any database&#039;s column using the $table variable. Eg: fetchFieldStruct(&#039;mycolumn&#039;, true, &#039;mytable&#039;);&lt;br /&gt;
* probeTable($table) helps you to check if a table exists in the database&lt;br /&gt;
* fetch_object($resource) will return a PHP object from a $resource, similarly to mysql_fetch_object() but with two advantages: it works on several DBMS (not only mysql), and it always return lowercase properties names (else depending on the configuration of the DBMS, the request can return uppercase or mixed case properties names).&lt;br /&gt;
* escape() and reverse_escape() are similare to mysql_real_escape but is reversible and DBMS-independent (so you can use it with other DBMSes).&lt;br /&gt;
&lt;br /&gt;
== Managing CustomFields structure ==&lt;br /&gt;
&lt;br /&gt;
The first step into CustomFields is to create and manage the structure of a custom field.&lt;br /&gt;
&lt;br /&gt;
=== Creating a custom field definition ===&lt;br /&gt;
&lt;br /&gt;
To create a custom field, you can use the addCustomField() method.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;addCustomField($fieldname, $type, $size, $nulloption, $defaultvalue = null, $constraint = null, $customtype = null, $customdef = null, $customsql = null, $fieldid = null, $extra = null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will take care of forging the (complicated) SQL query with the forgeSQLCustomField() method, and will return you an error if there was a problem.&lt;br /&gt;
&lt;br /&gt;
=== Updating a custom field definition ===&lt;br /&gt;
&lt;br /&gt;
To update a custom field, you can use the updateCustomField() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;&lt;br /&gt;
updateCustomField($fieldid, $fieldname, $type, $size, $nulloption, $defaultvalue, $constraint = null, $customtype = null, $customdef = null, $customsql = null, $extra = null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: the updateCustomField() and addCustomField() methods can both be used since updateCustomField() is in fact an alias for addCustomField(). CustomFields will automatically detect if a custom field already exists, and update it if that&#039;s true, or create it.&lt;br /&gt;
&lt;br /&gt;
Note2: updating a custom field will automatically take care of the constraints and foreign keys on its own, thus you can change the foreign key, or remove it altogether if set to null.&lt;br /&gt;
&lt;br /&gt;
=== Delete a custom field definition ===&lt;br /&gt;
&lt;br /&gt;
You can easily delete a custom field by column_name or by ordinal_position:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;deleteCustomField($id);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: constraints and foreign keys will also be deleted automatically so you shouldn&#039;t encounter any error.&lt;br /&gt;
&lt;br /&gt;
=== Fetch one custom field&#039;s SQL structure ===&lt;br /&gt;
&lt;br /&gt;
To get the SQL structure of a custom field, which can be quite useful to know for example its datatype or if it has a foreign key, or accepts null values, you can use the fetchFieldStruct() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
fields = $customfields-&amp;gt;fetchFieldStruct($id=null, $nohide=false, $table=null, $cachepath=null); // where $id can be a column_name, an ordinal_position from information_schema.columns or null if you want to fetch all the columns at once&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method will either return null, either a single field object, either an array on field objects.&lt;br /&gt;
&lt;br /&gt;
Note: this function will use caching by default to accelerate future results, but you can disable it, or even change the default cache path. For more informations please read the comments in the source code.&lt;br /&gt;
&lt;br /&gt;
=== Fetch several custom fields&#039; SQL structures ===&lt;br /&gt;
&lt;br /&gt;
Use the fetchAllFieldsStruct() method, which is an alias for fetchFieldStruct():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;fetchAllFieldsStruct($nohide=false, $table=null, $cachepath=null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: this method will always return an array of fields objects, even if only one is found (for more consistency), and this is the main difference with fetchFieldStruct(null).&lt;br /&gt;
&lt;br /&gt;
== Managing custom fields records ==&lt;br /&gt;
&lt;br /&gt;
Now that you know how to create and manage the structure of custom fields, we can proceed on to managing the records.&lt;br /&gt;
&lt;br /&gt;
=== Creating a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Simply use the create() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;create($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
$object must be an object containing as its properties the customfields data.&lt;br /&gt;
&lt;br /&gt;
For example, if you have a field named mycustomfield, your object must have $object-&amp;gt;mycustomfields defined.&lt;br /&gt;
&lt;br /&gt;
This method will store in the database ALL the custom fields that were created/edited and with a valid value (eg: an empty value for a field that does not accept null value is invalid) AT ONCE! So you can modify all the columns of one record at once, but not several records at once.&lt;br /&gt;
&lt;br /&gt;
Concretely, this is very useful with Dolibarr, because modules tends to store everything inside a single object generally called $object, and CustomFields tries to hook onto $object, so that you can just pass $object as is to create() and it will save all the custom fields.&lt;br /&gt;
&lt;br /&gt;
But you can also create an empty object with $object = new stdClass(); and then manually fill the custom fields records with whatever you want, and then call the create() method, this will work as well!&lt;br /&gt;
&lt;br /&gt;
You can also just modify only one column of one record, by just specifying one custom field column_name in your $object. The other custom fields that are not specified won&#039;t be created/modified.&lt;br /&gt;
&lt;br /&gt;
=== Updating a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Use the update() method, which is an alias for the create() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;update($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As noted above, update() is an alias of the create() method, which means that CustomFields will try to detect by itself if a record needs to be updated or created.&lt;br /&gt;
&lt;br /&gt;
=== Deleting a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Use the delete() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;delete($id); // where $id is = to fk_moduleid column, NOT rowid&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fetching a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Just use the fetch() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$records = $customfields-&amp;gt;fetch($id=null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where $id can either be = fk_moduleid (NOT rowid), or null to fetch all the records at once.&lt;br /&gt;
&lt;br /&gt;
This method will either return null, a single record object, or an array of records objects.&lt;br /&gt;
&lt;br /&gt;
=== Fetching all custom fields&#039; records ===&lt;br /&gt;
&lt;br /&gt;
Use the fetchAll() method, which is an alias for fetch():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$records = $customfields-&amp;gt;fetchAll();&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: this method will always return an array of records object, for more consistency (and this is the main difference with fetch(null) ).&lt;br /&gt;
&lt;br /&gt;
=== Fetching referenced records of a constrained field ===&lt;br /&gt;
&lt;br /&gt;
A constrained custom field points to another database table, and a value for this constrained field is the rowid of a record in the referenced table.&lt;br /&gt;
&lt;br /&gt;
Thus, the principal purpose of a constrained custom field is not the value of the field in itself (it&#039;s simply an integer), but rather the other fields available from the referenced table, the constrained field being in fact just a pointer.&lt;br /&gt;
&lt;br /&gt;
To fetch the remote record from the referenced table, use the fetchReferencedValuesList() of the CustomFields class:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$fkrecord = fetchReferencedValuesList($field, $id=null, $where=null, $allcolumns=false);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
&lt;br /&gt;
* $field is a custom field object (fetched using fetchFieldStruct() or fetchAllFieldsStruct()),&lt;br /&gt;
* $id is the rowid of the record to get, just like fetch() and fetchAll(). Can be null to fetch all remote records (or to use the $where clause).&lt;br /&gt;
* $where is a string without the WHERE clause (eg: &amp;quot;fk_facture=1&amp;quot;) to get finer control. In this case, you can use $id or set $id=null depending on your purpose. This is mainly a private argument to allow for cascade option.&lt;br /&gt;
* $allcolumns if false, it will fetch only the required columns to do the smart value substitution. If true, it will fetch all remote columns (useful for ODT substitution). If an array of strings, each string will be a column to fetch from the remote database.&lt;br /&gt;
&lt;br /&gt;
Note that this function is NOT recursive (it will fetch the records from the referenced table, and that&#039;s all, it won&#039;t fetch the custom fields of the referenced table nor recursively fetch the remote constrained custom fields). To do recursive fetching, see in the Facade API.&lt;br /&gt;
&lt;br /&gt;
Note2: this function automatically manages Smart Value Substitution depending of the $field-&amp;gt;column_name.&lt;br /&gt;
&lt;br /&gt;
=== Recursive fetching referenced records of a constrained field ===&lt;br /&gt;
&lt;br /&gt;
If you want to recursively fetch a constrained field (eg: constrainedfield1-&amp;gt;ref_table-&amp;gt;constrainedfield2-&amp;gt;ref_table2-&amp;gt;...), you need another function called fetchReferencedValuesRec(). Technically, this function is in the Facade API (inside /customfields/lib/customfields_aux.lib.php), but it isn&#039;t meant to be used by lambda users (contrary to customfields_fill_object()), but is mainly a helper function for the rest of the facade API.&lt;br /&gt;
&lt;br /&gt;
fetchReferencedValuesRec() works very similarly to fetchReferencedValuesList(), but it will work recursively.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$fkrecord = fetchReferencedValuesRec($customfields, $field, $id, $recursive=true)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where $customfields is the CustomFields instance for the module you are working on, $field is the constrained field you want to fetch, $id is the record&#039;s id you want to fetch, and $recursive enables or disables the recursion.&lt;br /&gt;
&lt;br /&gt;
Note that this function automatically manages Smart Value Substitution (and also recursively).&lt;br /&gt;
&lt;br /&gt;
Note2: This function tries to automatically avoid an infinite recursion loop by using a $blacklist: for a given constrained custom field, a table won&#039;t be visited twice. This means that for example you can do: invoice-&amp;gt;user-&amp;gt;socpeople-&amp;gt;invoice, this will work and every referenced fields will be fetched, but it will automatically stop the recursion at the second invoice, because else it could continue in a loop to user, socpeople, invoice, user, socpeople, etc.&lt;br /&gt;
&lt;br /&gt;
Note3: when adding recursively referenced fields, the key in the returned array will be prefixed by the name of the custom field that led to those remote fields. Thus, you can have something like &amp;quot;cf_myfield1_cf_remotefield1_cf_remotefield2&amp;quot; if you have a constrained field cf_myfield1-&amp;gt;cf_remotefield1-&amp;gt;cf_remotefield2.&lt;br /&gt;
&lt;br /&gt;
== Input forms for custom fields ==&lt;br /&gt;
&lt;br /&gt;
The API provides some neat ways to automatically manage the input forms for users to input new values, with the best adapted HTML form based on the custom field&#039;s data type.&lt;br /&gt;
&lt;br /&gt;
The following methods will print input fields that are best adapted to the SQL data type of your custom fields as well as figure out the possible values and even format them (for example: a not null enum will be printed as a HTML select list without an empty option; fetch automatically remote columns for constrained fields and show them in a select).&lt;br /&gt;
&lt;br /&gt;
So you don&#039;t have to bother how your data will be presented to the user nor how it will be processed.&lt;br /&gt;
&lt;br /&gt;
=== Printing an input form adapted to the field&#039;s data type ===&lt;br /&gt;
&lt;br /&gt;
showInputField() is a sort of converter customfieldstructure -&amp;gt; HTML input field.&lt;br /&gt;
&lt;br /&gt;
Use the &#039;&#039;&#039;showInputField()&#039;&#039;&#039; method to print the right input form for a custom field.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;showInputField($field, $currentvalue=null, $moreparam=&#039;&#039;, $ajax_php_callback=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
showInputField() takes 4 parameters:&lt;br /&gt;
&lt;br /&gt;
* $field is a CustomFields structure object (eg: returned by fetchFieldStruct()) and is necessary to detect the right datatype and the possible values.&lt;br /&gt;
* $currentvalue is the current value if you want to preselect a value (for example if the user already defined a value, you can reload it here). This is optional, in case it is not defined, either a null/empty value will be chosen, or either the default value defined when creating the custom field.&lt;br /&gt;
* $moreparam is used to add more HTML attributes, if you want for example to add some css styling or whatever you want.&lt;br /&gt;
* $ajax_php_callback is optional and allows to specify the relative url to the php callback script. If specified, an AJAX script will be automatically attached to this field (see AJAX-PHP callback below).&lt;br /&gt;
&lt;br /&gt;
showInputField() only prints ONE HTML input field, thus you have to enclose it/them by your own HTML form.&lt;br /&gt;
&lt;br /&gt;
eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print &#039;&amp;lt;form method=&amp;quot;get&amp;quot; action=&amp;quot;?&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
print &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;customtoken&amp;quot; value=&amp;quot;ABCDEFGH&amp;quot; /&amp;gt;&#039;; // example of a custom hidden HTML input field, that can be used to pass a token for example&lt;br /&gt;
// Loop over each custom fields (you have to load them before with $customfields-&amp;gt;fetchAllFieldsStruct()&lt;br /&gt;
foreach ($customfields-&amp;gt;fields as $field) {&lt;br /&gt;
    print $customfields-&amp;gt;showInputField($field, $currentvalue=null, $moreparam=&#039;&#039;);&lt;br /&gt;
}&lt;br /&gt;
print &#039;&amp;lt;/form&amp;gt;&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can also store the resulting HTML input field and process it by a regexp for example:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$myhtmlfield = $customfields-&amp;gt;showInputField($field, $currentvalue=null, $moreparam=&#039;&#039;);&lt;br /&gt;
preg_replace($myhtmlfield, ..., ...);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: for constrained fields, if the name follows the convention of &#039;&#039;&#039;Smart Value Substitution&#039;&#039;&#039; (ie: remote columns names are included in the custom field&#039;s name), showInputField() will &#039;&#039;&#039;automatically fetch the remote values, format them, and present them&#039;&#039;&#039; in a HTML select.&lt;br /&gt;
&lt;br /&gt;
=== Printing an input form for a custom field ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;showInputForm()&#039;&#039;&#039; is a function used to print a single (and only one!) custom field with an adapted HTML input field, but enclosed by a HTML form. This is used for example when you want to allow to edit a single custom field but not the others.&lt;br /&gt;
&lt;br /&gt;
showInputForm() is in fact a wrapper for showInputField(), thus it will call showInputField() and simply enclose it by a form.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;showInputForm($id, $field, $currentvalue=null, $idvar=&#039;id&#039;, $page=null, $moreparam=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== AJAX-PHP callback ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;showInputFieldAjax()&#039;&#039;&#039; will output an AJAX script (using jQuery) to send and receive data to and from a specified php script. The AJAX script will automatically manage HTML form input&#039;s updating as long as the received data follows some convention.&lt;br /&gt;
&lt;br /&gt;
An AJAX script can be attached to any field (including non-CustomFields) by using the method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;showInputFieldAjax($id, $php_callback_url, $on_func=&amp;quot;change&amp;quot;, $request_type=&amp;quot;post&amp;quot;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
* $id is the HTML id of the field you want to attach the AJAX to.&lt;br /&gt;
* $phpcallback the relative path from Dolibarr&#039;s htdocs root folder to the php script that will receive and send back data.&lt;br /&gt;
* $on_func is the Javascript or jQuery event that should trigger the AJAX script (by default on change).&lt;br /&gt;
* $request_type is the HTML query type that will be used to send the data to the PHP callback (either &amp;quot;get&amp;quot; or &amp;quot;post&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
The AJAX script will automatically manage sending/receiving of data:&lt;br /&gt;
&lt;br /&gt;
* automatically send all form&#039;s inputs values, as well as the current calling field&#039;s name and value. The sending happens on change by default, but another event can be set using the $on_event argument. Data will be sent using standard GET or POST.&lt;br /&gt;
* Upon reception of data from the php script, the AJAX script will automatically parse the data and update the HTML fields.&lt;br /&gt;
&lt;br /&gt;
The PHP callback script must send back to AJAX the data in JSON encoded format, and with with a specific format:&lt;br /&gt;
&lt;br /&gt;
* the data must be an associative array, where each entry&#039;s key is the name (not ID!) of an HTML field to update.&lt;br /&gt;
* each entry&#039;s value is an associative array, where each sub-entry&#039;s key is the action to do. Available actions: &#039;options&#039;, &#039;value&#039;, &#039;html&#039;, &#039;alert&#039;.&lt;br /&gt;
* each sub-entry&#039;s value is the value for this action and HTML field.&lt;br /&gt;
&lt;br /&gt;
Thus, this approach allows to generically support a wide range of updating actions, managed automatically.&lt;br /&gt;
&lt;br /&gt;
You, the implementer, have just to edit the PHP callback script and return an associative array defining what action you want for each field you want to update. This is exactly what has been implemented in the Custom AJAX Functions library, which simplifies even more your job by managing automatically the step described here (generating AJAX and formatting data), so that you can focus on &#039;&#039;&#039;making the data&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Printing custom fields in documents ==&lt;br /&gt;
&lt;br /&gt;
The API can also automatically manage the printing and formatting of custom fields in your ODT, PDF and HTML documents.&lt;br /&gt;
&lt;br /&gt;
=== Printing in HTML ===&lt;br /&gt;
&lt;br /&gt;
You can either just print the value as-is, or pass them through the printField() or simpleprintField() function to format them (very useful for constrained fields) - see below.&lt;br /&gt;
&lt;br /&gt;
=== Printing in documents ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;printField()&#039;&#039;&#039; is used to properly print and translate a custom field&#039;s record&#039;s value. Generally, you can just print the custom field&#039;s value as-is, but in a few cases (eg for boolean types, or constrained fields), using the printField() method will more properly print the value.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;printField($field, $value, $outputlangs=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
* $field is the custom field&#039;s SQL structure as an object (eg: returned by fetchFieldStruct()).&lt;br /&gt;
* $value is the custom field&#039;s value for the current record (eg: $customfields-&amp;gt;fields-&amp;gt;myfield).&lt;br /&gt;
* $outputlangs is the Translate object for the target language.&lt;br /&gt;
&lt;br /&gt;
For example, with a constrained field with Smart Value Substitution, instead of printing the id like 1, it will print the full name of user like FirstName Name.&lt;br /&gt;
&lt;br /&gt;
This method will also translate a value if a translation is found in the language files.&lt;br /&gt;
&lt;br /&gt;
Another method &#039;&#039;&#039;printFieldPDF()&#039;&#039;&#039; can be used to properly print a value for a PDF (properly encoding the characters).&lt;br /&gt;
&lt;br /&gt;
Powertip: this function can also be used to get the Smart Value Substituted value of the field (instead of printing the rowid) in the field is constrained.&lt;br /&gt;
&lt;br /&gt;
=== Simple printing in documents ===&lt;br /&gt;
&lt;br /&gt;
If you don&#039;t want to have to manually fetch the structure of every custom field whose you want to print the value, you can then use a simplifier method called &#039;&#039;&#039;simpleprintField()&#039;&#039;&#039; which will take as input a custom field name instead of a custom field SQL structure object.&lt;br /&gt;
&lt;br /&gt;
This method is just a simplifier (Facade) wrapper for the printField() method, and thus will have the same advantages (fetch constrained fields, values translation, etc.).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;simpleprintField($fieldname, $value, $outputlangs=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
* $fieldname is the custom field&#039;s name (eg: &amp;quot;myfield&amp;quot;).&lt;br /&gt;
* $value is the custom field&#039;s value for the current record (eg: $customfields-&amp;gt;fields-&amp;gt;myfield).&lt;br /&gt;
* $outputlangs is the Translate object for the target language.&lt;br /&gt;
&lt;br /&gt;
Another method &#039;&#039;&#039;simpleprintFieldPDF()&#039;&#039;&#039; can be used to properly print a value for a PDF (properly encoding the characters).&lt;br /&gt;
&lt;br /&gt;
Powertip: this function can also be used to get the Smart Value Substituted value of the field (instead of printing the rowid) in the field is constrained.&lt;br /&gt;
&lt;br /&gt;
=== Find the label ===&lt;br /&gt;
&lt;br /&gt;
Finding the label of a custom field might not be as simple as it seems. First, values of properties objects may be easily used, but their name (key) is a worst job. Secondly, custom fields&#039; labels can be translated, and the following functions are expressly made for that purpose. Thirdly, for PDF and some other documents you have to encode the characters if you want them to properly be drawn.&lt;br /&gt;
&lt;br /&gt;
So if you want to print a custom field&#039;s label with translation (multilingual) support, just use the &#039;&#039;&#039;findLabel()&#039;&#039;&#039; method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$translatedfieldname = $customfields-&amp;gt;findLabel($fieldname, $outputlangs = &#039;&#039;); // where $fieldname is just a custom field label (eg: cf_mycustomfield or mycustomfield, both will work) and $outputlangs is the target language object (see the Translate class native of Dolibarr)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As for the other printing methods, you also have the &#039;&#039;&#039;findLabelPDF()&#039;&#039;&#039; wrapper method to easily print labels that with characters encoded for PDF.&lt;br /&gt;
&lt;br /&gt;
= Facade API =&lt;br /&gt;
&lt;br /&gt;
The Facade API was made to ease the usage of the CustomFields class with the Dolibarr system. It provides a neat way to do many operations with single commands, and will automatically manage the loading of the CustomFields class, and its usage with many optimizations.&lt;br /&gt;
&lt;br /&gt;
== customfields_fill_object() ==&lt;br /&gt;
&lt;br /&gt;
You can load the Facade API and then use the &#039;&#039;&#039;customfields_fill_object()&#039;&#039;&#039; to automatically fetch the fields structures and the records, and also automatically link to remote fields when a constrained field is found. This is an all-in-one solution to easily fetch CustomFields datas, and is also optimized to fully use caching and other special properties of the CustomFields class.&lt;br /&gt;
&lt;br /&gt;
Concretely, you just have to include the Facade API, then pass the customfields_fill_object() with a Dolibarr $object, and the Facade API will return a CustomFields object with appended the custom fields records and also the custom fields&#039; SQL structures.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$customfields = customfields_fill_object($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* $object will then include the custom fields records associated with this object (eg: $object-&amp;gt;customfields-&amp;gt;cf_yourfield).&lt;br /&gt;
* $customfields will be a CustomFields object containing the fields structures (eg: $customfields-&amp;gt;fields-&amp;gt;yourfield).&lt;br /&gt;
&lt;br /&gt;
You can also call any CustomFields function if you need to using the $customfields object, eg: $customfields-&amp;gt;simpleprintField($object-&amp;gt;customfields-&amp;gt;cf_yourfield);&lt;br /&gt;
&lt;br /&gt;
Note: $object can also be a dummy object containing only two properties that are required: $object-&amp;gt;id and $object-&amp;gt;table_element.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will now describe the full specification of this function:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$customfields = customfields_fill_object(&amp;amp;$object,$fromobject = null, $outputlangs = null, $prefix = null,$pdfformat = false);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* $object is the object where the customfields will be saved&lt;br /&gt;
* $fromobject is the object from where the customfields will be fetched (this can be a different module than $object, or can be null to be the same as $object)&lt;br /&gt;
* $outputlangs is the target language for translation of fields value (if empty, the default current global translation object will be used)&lt;br /&gt;
* $prefix allows you to save the custom fields in a sub-object. By default, when $prefix=null, custom fields are saved in the path $object-&amp;gt;customfields, but when you specify a prefix, custom fields will be saved inside $object-&amp;gt;customfields-&amp;gt;$prefix. This may be useful if you want to store multiple object&#039;s custom fields or records in the same $object.&lt;br /&gt;
* $pdfformat defines if you want a special formatting for the values: if null, there&#039;s no formatting, if false, there is a simple formatting (eg: translation of fields&#039; values), if true then the formatting will be adapted for PDF templating.&lt;br /&gt;
&lt;br /&gt;
For more informations on pratical usages of this function, please [[Module_CustomFields#Implementing_in_php_code_.28dolibarr_core_modules_or_your_own_module.29|read the user documentation on how to implement CustomFields in php code]] or [[Module_CustomFields_Cases#Linking_Dolibarr_objects_from_two_different_modules|read an example case of linking two objects of different modules together]].&lt;br /&gt;
&lt;br /&gt;
== customfields_fill_object_lines() ==&lt;br /&gt;
&lt;br /&gt;
Similar to customfields_fill_object() for normal custom fields, there is also a &#039;&#039;&#039;customfields_fill_object_lines()&#039;&#039;&#039; to fetch custom fields associated with lines of products/services. This function is also optimized and it is strongly advised to use it and to not try to fetch manually this kind of custom fields (ie: associated with lines of products/services rather than objects), because there are a lot of optimizations going on behind the scene and that are very hard to do manually.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields for lines of products/services (you can then access them by doing $object-&amp;gt;customfields-&amp;gt;lines-&amp;gt;$lineid-&amp;gt;cf_yourfield)&lt;br /&gt;
$customfields = customfields_fill_object_lines($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Requirements: $object can be a dummy object but must contain at least 2 properties: $object-&amp;gt;id and $object-&amp;gt;table_element_line.&lt;br /&gt;
&lt;br /&gt;
For more informations on pratical usages of this function, please [[Module_CustomFields#Implementing_in_php_code_.28dolibarr_core_modules_or_your_own_module.29|read the user documentation on how to implement CustomFields in php code]].&lt;br /&gt;
&lt;br /&gt;
= Architecture =&lt;br /&gt;
Here is a full list of the CustomFields packaged files with a short description (for a more in-depth view just crawl the source files, they are full of comments):&lt;br /&gt;
&lt;br /&gt;
== Core files ==&lt;br /&gt;
files that are necessary for the CustomFields to work, they contains the core functions&lt;br /&gt;
&lt;br /&gt;
* /customfields/admin/customfields.php --- Administrator&#039;s configuration panel : this is where you create and manage the custom fields definitions&lt;br /&gt;
* /customfields/class/actions_customfields.class.php --- Hooks class : used to hook into Dolibarr core modules without altering any core file (can be used to hook into your own modules too). Also used to preprocess the parameters before calling the printforms library.&lt;br /&gt;
* /customfields/class/customfields.class.php --- Core class : every database action is made here in this class. You can find some printing functions because they are very generic.&lt;br /&gt;
* /customfields/conf/conf_customfields.lib.php --- Configuration file : contains the main configurable variables to adapt CustomFields to your needs or to expand its support to other modules and more native sql types.&lt;br /&gt;
* /customfields/conf/conf_customfields_func.lib.php --- Auxiliary functions library to read and manage config variables&lt;br /&gt;
* /customfields/core/modules/modCustomFields.class.php --- Dolibarr&#039;s module definition file : this is a core file necessary for Dolibarr to recognize the module and to declare the hooks to Dolibarr (but it does not store anything else than meta-informations).&lt;br /&gt;
* /customfields/core/substitutions/functions_customfields.lib.php --- CustomFields substitution class for ODT generation : necessary to support customfields tags in ODT files&lt;br /&gt;
* /customfields/core/triggers/interface_50_modCustomFields_SaveFields.class.php --- Core triggers file : this is where the actions on records are managed. This is an interface between other modules and CustomFields management. You should not modify this file but you should add your triggers in $triggersarray inside conf_customfields.lib.php, but if you really have a special need and know what you do, you can edit this file to directly add your own triggers. Also, if CustomFields are shown in the forms but cannot be modified nor saved, then probably this trigger file is not detected by Dolibarr (maybe you can try to lower the number 50 to raise the priority?).&lt;br /&gt;
* /customfields/fields/customfields_fields_extend.lib.php.example --- Overloading functions library with users functions: this is where implementers can use their own php code to manage the custom fields they want (one function per action and per field).&lt;br /&gt;
* /customfields/fields/customfields_fields_ajax_custom.lib.php.example --- Custom AJAX functions library with users functions: this is where implementers can use their own php code to manage the custom fields cascading and AJAX calls, similarly to the Overloading functions library (one function per action and per field).&lt;br /&gt;
* /customfields/langs/code_CODE/customfields.lang --- Core language file : this is where you can translate the admin config panel (data types names, labels, descriptions, etc.)&lt;br /&gt;
* /customfields/langs/code_CODE/customfields-user.lang --- User defined language file : this is where you can store the labels and values of your custom fields (see the related chapter)&lt;br /&gt;
* /customfields/lib/customfields_aux.lib.php --- Simplifier library to easily use CustomFields class to populate an $object with custom fields datas (Facade design pattern). This is what you should use for most of your needs.&lt;br /&gt;
* /customfields/lib/customfields_printforms.lib.php --- Core printing library for records : contains only printing functions, there&#039;s not really any core functions inside but it allows to manage the printing of the custom fields records and their editing.&lt;br /&gt;
* /customfields/lib/customfields_ajax_wrapper.lib.php --- AJAX wrapper library to automatically manage cascading at objects creation. This isn&#039;t really necessary for CustomFields to function, this is more an addition to allow dynamical AJAX updating, but CustomFields core functionalities can work without this library.&lt;br /&gt;
* /customfields/sql/*.sql --- Unused (the tables are created directly via a function in the customfields.class.php) - but the sql files are still valid, they were used as squeletton in the first designs.&lt;br /&gt;
&lt;br /&gt;
== PDFTest module ==&lt;br /&gt;
optional module to append a page listing all custom fields values on newly generated PDF documents. To be used to test if custom fields are working correctly before making one&#039;s own template.&lt;br /&gt;
&lt;br /&gt;
* /customfieldspdftest/class/actions_customfieldspdftest.class.php --- Main functions library and hooks class, to hook onto pdf generation. Contains the PDFTest functions.&lt;br /&gt;
* /customfieldspdftest/core/modules/modCustomFieldsPDFTest.class.php --- Module descriptor for Dolibarr&lt;br /&gt;
&lt;br /&gt;
== Runtime outline ==&lt;br /&gt;
&lt;br /&gt;
Here is an outline of how CustomFields works: what functions will be called and why:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At admin page:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* No external function call (apart for some icons or needed admin functions like for tabs), everything that is done are sql queries via CustomFields class (to fetch and modify the sql columns in the customfields tables).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At printing:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Module&#039;s hook formObjectOptions calls Dolibarr&#039;s hookmanager&lt;br /&gt;
* Hookmanager calls CustomFields /class/actions_customfields.class.php, function formObjectOptions() of CustomFields&lt;br /&gt;
* Preprocess a few parameters (mainly the $action) and calls customfields_print_forms()&lt;br /&gt;
* customfields_print_forms() preprocess all the hook&#039;s parameters and assimilate them with the CustomFields&#039;s configuration parameters (there&#039;s no connection to the database at this stage, only parameters preprocessing), then calls /lib/customfields_printforms.lib.php, functions customfields_print_creation_form() or customfields_print_datasheet_form&lt;br /&gt;
* both customfields_print_creation_form() lookup the database using CustomFields class and prints empty custom fields; customfields_print_datasheet_form() lookup the database using CustomFields class, fetch the values of the custom fields and prints them;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At edition:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Same steps as &#039;&#039;&#039;At printing&#039;&#039;&#039;&lt;br /&gt;
* then customfields_print_datasheet_form() process the custom field being edited differently than the others: if editing, it prints the html input field (to enter a new value), or if saving it just saves the data into the database by calling the CUSTOMFIELDS_MODIFY trigger and then continue to print the other fields normally.&lt;br /&gt;
* CUSTOMFIELDS_MODIFY trigger calls $customfields-&amp;gt;update($newrecord);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At creation (saving):&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Module&#039;s creation trigger MYMODULE_CREATE calls Dolibarr&#039;s run_trigger()&lt;br /&gt;
* Dolibarr run_trigger() calls CustomFields trigger file /core/trigger/interface_50_modCustomFields_SaveFields.class.php, function run_trigger()&lt;br /&gt;
* CustomFields&#039;s run_trigger() assimilate the given parameters with the CustomFields&#039;s config parameters, and if the trigger is found in $triggersarray, then the appropriate action is taken (by default: _CREATE or another standard action if recognized), if not in $triggersarray, the function will still try to automatically detect the appropriate action by detecting MYMODULE_ALISTOFACTIONS with a regular expression.&lt;br /&gt;
* The appropriate action is then done (creation in this case) by calling $customfields-&amp;gt;create($record).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At edition or creation of product lines:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Similar to &#039;&#039;&#039;At edition&#039;&#039;&#039; but with hooks: formCreateProductOptions and formEditProductOptions.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At ODT document generation and substitution:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Module&#039;s ODT generator calls for Dolibarr&#039;s function complete_substitution_array(), which then calls for CustomFields substitution file /core/substitution/functions_customfields.lib.php, function customfields_completesubstitutionarray()&lt;br /&gt;
* CustomFields&#039;s function customfields_completesubstitutionarray() calls the simplifier library customfields_aux.lib.php, function customfields_fill_object() and make the substitution inside the $substitutionarray.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At ODT document generation and substitution for product lines:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Same as the previous point but this function is called for every product lines, and thus the data is cached. Also the simplifier function is here called customfields_fill_object_lines(), and the CustomFields&#039;s substitution function called customfields_completesubstitutionarray_lines().&lt;br /&gt;
&lt;br /&gt;
= How to add the support of a new module =&lt;br /&gt;
&lt;br /&gt;
There are two cases: either the module already implements hooks, and you just have to configure CustomFields to support it, or either the module does not implements hooks nor triggers.&lt;br /&gt;
&lt;br /&gt;
This chapter will guide you through the steps to make any module supported by CustomFields (be it a Dolibarr core module or your own module).&lt;br /&gt;
&lt;br /&gt;
== Preliminary test ==&lt;br /&gt;
&lt;br /&gt;
This preliminary test will allow you to know at a glance how much work needs to be done in order to make your module supported by CustomFields, and it will also give you all the config parameters if they are available.&lt;br /&gt;
&lt;br /&gt;
Important note: this test will only give data about hooks, which is the main part to make CustomFields work for your module. But for triggers, there is no way to test, so you will have to check the triggers manually (either by testing if the custom fields are saved at creation page, or by looking at the source code of the objects classes).&lt;br /&gt;
&lt;br /&gt;
Edit the php script of the module (check the URL when you access the module&#039;s datasheet or creation page - attention both are two different pages, so you have to do this twice), then put this at the very &#039;&#039;&#039;end&#039;&#039;&#039; of the php script:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// CustomFields debuglines to show config parameters for current module&lt;br /&gt;
print(&amp;quot;&amp;lt;pre&amp;gt;&amp;quot;); // html nice output&lt;br /&gt;
print(&amp;quot;table_element: {$object-&amp;gt;table_element} - table_element_line: {$object-&amp;gt;table_element_line} - contexts: &amp;quot;);&lt;br /&gt;
print_r($hookmanager-&amp;gt;contextarray);&lt;br /&gt;
print(&amp;quot; - conf hooks_modules: &amp;quot;);&lt;br /&gt;
print_r($conf-&amp;gt;hooks_modules);&lt;br /&gt;
print(&amp;quot; - hooks attached: &amp;quot;);&lt;br /&gt;
print_r($hookmanager-&amp;gt;hooks);&lt;br /&gt;
print(&amp;quot; - user rights: &amp;quot;);&lt;br /&gt;
print_r($user-&amp;gt;rights);&lt;br /&gt;
print(&amp;quot;&amp;lt;/pre&amp;gt;&amp;quot;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now reload the page, and scroll at the very bottom. You should see some informations about the module.&lt;br /&gt;
&lt;br /&gt;
Depending on the data available, you will know what you have to do:&lt;br /&gt;
&lt;br /&gt;
* If all the datas is available, then it&#039;s ok, jump to [[Module_CustomFields_Dev#Configuring_CustomFields_to_support_the_module]] and copy these datas to the parameters of conf_customfields.lib.php&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;table_element&#039;&#039;&#039;: $object is not defined, try to find how is called the main object variable and replace $object by this variable in the debug code given above (eg: in Agenda, the object variable is called $agenda and $agendacomm).&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;table_element_line&#039;&#039;&#039;: either there&#039;s no products lines for this module, either the main object variable is not called $object (see the previous point). One last case is that the object is not standard and does not store table_element_line (the name of the table that stores its products lines), in which case you have to manually add it in the class (eg: $this-&amp;gt;table_element_line = &#039;something&#039;).&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;contexts&#039;&#039;&#039;: the hookmanager is not defined, and probably there&#039;s no hook at all in this module, so you&#039;ll have to implement them by yourself. Jump to [[Module_CustomFields_Dev#Hooks]].&lt;br /&gt;
&lt;br /&gt;
* If there is a list of &#039;&#039;&#039;contexts&#039;&#039;&#039; but the module cannot be found in the list: the hookmanager is defined but the module has not defined a context, you still have to implement the hooks. Jump to [[Module_CustomFields_Dev#Hooks]].&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;conf hooks_modules&#039;&#039;&#039;: this variable is not necessary, empty or not it doesn&#039;t matter. But if you see customfields and/or customfieldspdftest inside the list, it is a confirmation that CustomFields indeed is hooking the module correctly.&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;hooks attached&#039;&#039;&#039;, or CustomFields cannot be found in the list: CustomFields has not been properly configured to hook to this module, or you forgot to disable then re enable CustomFields in order to update the CustomFields&#039;s hooks. Jump to [[Module_CustomFields_Dev#Configuring_CustomFields_to_support_the_module]].&lt;br /&gt;
&lt;br /&gt;
* If there is no &#039;&#039;&#039;$user-&amp;gt;rights&#039;&#039;&#039;: there is either no standard security implemented in this module, or either the variable is called with another name. If there is no security, you don&#039;t need to configure it in CustomFields, but if there is a security, you need to find how the variable is called (like $object can get another name sometimes).&lt;br /&gt;
&lt;br /&gt;
* If there is &#039;&#039;&#039;$user-&amp;gt;rights&#039;&#039;&#039; but the module can&#039;t be found in the list: either the name for the rights is different (this often happens, the rights array is not yet standardized), or either the rights are hardcoded into the module&#039;s code (rights should be announced to the $user-&amp;gt;rights array, but sometimes it happens that the dev forgot to do so, and thus the right is entirely hardcoded and you can&#039;t find it in the dynamic rights array). In any of these two cases, you will have to manually read the module&#039;s code to find the right. Also, maybe there is no security implemented, but since $user-&amp;gt;rights is declared this is less probable.&lt;br /&gt;
&lt;br /&gt;
Here is an example of output on a correctly hooked module (invoice):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
table_element: facture - table_element_line: facturedet - contexts: Array&lt;br /&gt;
(&lt;br /&gt;
    [0] =&amp;gt; searchform&lt;br /&gt;
    [1] =&amp;gt; leftblock&lt;br /&gt;
    [2] =&amp;gt; toprightmenu&lt;br /&gt;
    [3] =&amp;gt; invoicecard&lt;br /&gt;
)&lt;br /&gt;
 - conf hooks_modules: Array&lt;br /&gt;
(&lt;br /&gt;
    [customfields] =&amp;gt; invoicecard:propalcard:productcard:ordercard:thirdpartycard:contactcard:ordersuppliercard:invoicesuppliercard:membercard:actioncard:projectcard:projecttaskcard:contractcard:interventioncard:doncard:tripsandexpensescard:taxvatcard:expeditioncard:invoicecard:propalcard:ordercard:ordersuppliercard:invoicesuppliercard&lt;br /&gt;
    [customfieldspdftest] =&amp;gt; Array&lt;br /&gt;
        (&lt;br /&gt;
            [0] =&amp;gt; pdfgeneration&lt;br /&gt;
            [1] =&amp;gt; ordersuppliercard&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
)&lt;br /&gt;
 - hooks attached: Array&lt;br /&gt;
(&lt;br /&gt;
    [invoicecard] =&amp;gt; Array&lt;br /&gt;
        (&lt;br /&gt;
            [customfields] =&amp;gt; ActionsCustomFields Object&lt;br /&gt;
                (&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
)&lt;br /&gt;
 - user rights: stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [user] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [user] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [lire] =&amp;gt; 1&lt;br /&gt;
                    [creer] =&amp;gt; 1&lt;br /&gt;
                    [password] =&amp;gt; 1&lt;br /&gt;
                    [supprimer] =&amp;gt; 1&lt;br /&gt;
                    [export] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [self] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [creer] =&amp;gt; 1&lt;br /&gt;
                    [password] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [user_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [readperms] =&amp;gt; 1&lt;br /&gt;
                    [write] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [self_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [readperms] =&amp;gt; 1&lt;br /&gt;
                    [writeperms] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [group_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [read] =&amp;gt; 1&lt;br /&gt;
                    [readperms] =&amp;gt; 1&lt;br /&gt;
                    [write] =&amp;gt; 1&lt;br /&gt;
                    [delete] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    [facture] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [lire] =&amp;gt; 1&lt;br /&gt;
            [creer] =&amp;gt; 1&lt;br /&gt;
            [invoice_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [unvalidate] =&amp;gt; 1&lt;br /&gt;
                    [send] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [valider] =&amp;gt; 1&lt;br /&gt;
            [paiement] =&amp;gt; 1&lt;br /&gt;
            [supprimer] =&amp;gt; 1&lt;br /&gt;
            [facture] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [export] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    [propale] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [lire] =&amp;gt; 1&lt;br /&gt;
            [creer] =&amp;gt; 1&lt;br /&gt;
            [valider] =&amp;gt; 1&lt;br /&gt;
            [propal_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [send] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [cloturer] =&amp;gt; 1&lt;br /&gt;
            [supprimer] =&amp;gt; 1&lt;br /&gt;
            [export] =&amp;gt; 1&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    [produit] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [lire] =&amp;gt; 1&lt;br /&gt;
            [creer] =&amp;gt; 1&lt;br /&gt;
            [supprimer] =&amp;gt; 1&lt;br /&gt;
            [export] =&amp;gt; 1&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    etc... a lot of other rights...&lt;br /&gt;
&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When you are finished with implementing the support of CustomFields in the module, you should delete these debug lines.&lt;br /&gt;
&lt;br /&gt;
== Implementing the support of CustomFields in the module ==&lt;br /&gt;
&lt;br /&gt;
If there is a module you want to implement the support of CustomFields, but does not yet has the required components, here is a guide on how to implement these components.&lt;br /&gt;
&lt;br /&gt;
=== Hooks ===&lt;br /&gt;
&lt;br /&gt;
Implementing hooks will allow custom fields to appear on your module&#039;s page. They will also be editable and they will save the data. For creation, you need to use a trigger (see the subchapter below).&lt;br /&gt;
&lt;br /&gt;
First you should be familiar with the way that hooks are implemented in modules. If you don&#039;t, please first [[Hooks_system#Implementation|read the hooks documentation]].&lt;br /&gt;
&lt;br /&gt;
An example case of implementation of hooks into a Dolibarr core file can be found in the [[Module_CustomFields_Cases|CustomFields Example Cases wiki page]].&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Datasheet hook&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
To implement CustomFields in your module, you must initialize the hook manager, then use the following hook context:&lt;br /&gt;
* formObjectOptions&lt;br /&gt;
&lt;br /&gt;
This hook must be placed everywhere in your code where you want custom fields to appear (eg: on the creation page and datasheet view page).&lt;br /&gt;
&lt;br /&gt;
Note: Generally, you will have to place the hook just before the end of a table tag (&#039;&#039;&#039;&amp;lt;/table&amp;gt;&#039;&#039;&#039;), so if you don&#039;t know where to place it (eg: if you&#039;re implementing CustomFields on a Dolibarr core module), just search for this tag in the page shown in the url (eg: /htdocs/commande/fiche.php).&lt;br /&gt;
&lt;br /&gt;
Eg: First load the hookmanager at the beginning of your php script:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Initialize technical object to manage hooks of thirdparties. Note that conf-&amp;gt;hooks_modules contains array array&lt;br /&gt;
include_once(DOL_DOCUMENT_ROOT.&#039;/core/class/hookmanager.class.php&#039;);&lt;br /&gt;
$hookmanager=new HookManager($db);&lt;br /&gt;
$hookmanager-&amp;gt;initHooks(array(&#039;yourmodule&#039;));&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then place the hook as many times as required:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// ... a lot of php code ...&lt;br /&gt;
&lt;br /&gt;
print &#039;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&#039;; // end of the last row of a table&lt;br /&gt;
&lt;br /&gt;
// Other attributes&lt;br /&gt;
$parameters=array(&#039;colspan&#039; =&amp;gt; &#039; colspan=&amp;quot;3&amp;quot;&#039;); // specifying the colspan is optional but will help CustomFields know how to correctly print the table&lt;br /&gt;
$reshook=$hookmanager-&amp;gt;executeHooks(&#039;formObjectOptions&#039;,$parameters,$object,$action);    // Note that $action and $object may have been modified by hook&lt;br /&gt;
&lt;br /&gt;
print &#039;&amp;lt;/table&amp;gt;&#039;; // end of the table, this is what you&#039;re looking for, place the hook just above&lt;br /&gt;
&lt;br /&gt;
// ... a lot of php code ... redo the same if you find another &amp;lt;/table&amp;gt; and there&#039;s a page where there&#039;s no custom fields but you want to place custom fields on there&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note2: If after implementing the hook you receive an error or custom fields don&#039;t appear, you may have to change $object to another variable, depending on what is called the main object (eg: in Agenda it&#039;s called $action and $actioncomm).&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Lines hooks&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
For product lines, you should anyway use the product lines template files: htdocs/core/tpl/freeproductline_edit.tpl.php&lt;br /&gt;
&lt;br /&gt;
If you don&#039;t, you can still manually implement the required hooks, what is required is that you supply $line (which is the current line object being processed) in the $parameters array:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$parameters=array(&#039;line&#039;=&amp;gt;$line,&#039;fk_parent_line&#039;=&amp;gt;$line-&amp;gt;fk_parent_line);&lt;br /&gt;
&lt;br /&gt;
echo $hookmanager-&amp;gt;executeHooks(&#039;formEditProductOptions&#039;,$parameters,$this,$action);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hooks names:&lt;br /&gt;
* formEditProductOptions&lt;br /&gt;
* formCreateProductOptions ($line is not required here, because we create a new product line! you can leave $parameters=array(); empty).&lt;br /&gt;
Note: there&#039;s no hook for viewing, because showing the customfields will clutter the visual field, but if you really want you can also do it by adding a formViewProductOptions hook.&lt;br /&gt;
&lt;br /&gt;
=== Triggers ===&lt;br /&gt;
&lt;br /&gt;
First you need to know how to implement triggers in Dolibarr. For more informations about how to implement triggers in your module, please read the [[Triggers#Manage_and_create_a_new_trigger.27s_action|triggers wiki page]].&lt;br /&gt;
&lt;br /&gt;
An example case of implementation of hooks into a Dolibarr core file can be found in the [[Module_CustomFields_Cases|CustomFields Example Cases wiki page]].&lt;br /&gt;
&lt;br /&gt;
Implementing triggers on your module will allow CustomFields to save the fields at creation (datasheet edition is already managed by hooks).&lt;br /&gt;
&lt;br /&gt;
You need to implement 3 triggers:&lt;br /&gt;
- on creation of object - eg: YOURMODULE_CREATE&lt;br /&gt;
- on creation of lines - eg: YOURMODULELINE_INSERT (optional, only if your module has product lines)&lt;br /&gt;
- on update of lines - eg: YOURMODULELINE_UPDATE (optional, only if your module has product lines)&lt;br /&gt;
&lt;br /&gt;
Note: you need to supply the sql id of the newly created object before calling the trigger.&lt;br /&gt;
&lt;br /&gt;
Eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$this-&amp;gt;id = $this-&amp;gt;db-&amp;gt;last_insert_id(MAIN_DB_PREFIX.&amp;quot;yourmodule_table_element&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
// Appel des triggers&lt;br /&gt;
include_once(DOL_DOCUMENT_ROOT . &amp;quot;/core/class/interfaces.class.php&amp;quot;);&lt;br /&gt;
$interface=new Interfaces($this-&amp;gt;db);&lt;br /&gt;
$result=$interface-&amp;gt;run_triggers(&#039;YOURMODULE_CREATE&#039;,$this,$user,$langs,$conf);&lt;br /&gt;
if ($result &amp;lt; 0) {&lt;br /&gt;
    $error++; $this-&amp;gt;errors=$interface-&amp;gt;errors;&lt;br /&gt;
}&lt;br /&gt;
// Fin appel triggers&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note2: if it&#039;s a Dolibarr core module, you can easily find the right place to put the trigger by searching for &#039;create sql&#039; in the class subfolder (eg: URL is /htdocs/commande/fiche.php, the class is in /htdocs/commande/class/commande.class.php).&lt;br /&gt;
&lt;br /&gt;
Note3: CustomFields also supports a few other triggers like _CLONE, _UPDATE, _MODIFY, but they are not required and may not work depending on how your module works to manage these actions.&lt;br /&gt;
&lt;br /&gt;
== Configuring CustomFields to support the module ==&lt;br /&gt;
&lt;br /&gt;
=== Preliminary work ===&lt;br /&gt;
&lt;br /&gt;
You need to get the parameters of this module, and you will then put them in the CustomFields configuration file (conf_customfields.lib.php).&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Getting the parameters automatically&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
You can use the [[Module_CustomFields_Dev#Preliminary_test|preliminary test]] to easily get all those parameters.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Getting the parameters manually&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
To configure CustomFields to support a module, you need at least 5 parameters:&lt;br /&gt;
* the module&#039;s table_name (name of the sql table where the module stores its datas)&lt;br /&gt;
* the module&#039;s context (name given by the module to the hookmanager)&lt;br /&gt;
* the module&#039;s idvar (name of the variable that holds the sql row id of the record)&lt;br /&gt;
* the module&#039;s trigger action(s) (triggers on creation and update, see the related chapter above)&lt;br /&gt;
* the module&#039;s rights (for security purposes)&lt;br /&gt;
&lt;br /&gt;
To get these parameters manually, you can take a look inside the code of the module&#039;s files you want to implement:&lt;br /&gt;
* module&#039;s table name: either find it by yourself in the database (looks like llx_themodulename) or by printing it in actions_customfields.class.php by adding &#039;&#039;&#039;print_r($parameters)&#039;&#039;&#039; and search for $parameters-&amp;gt;table_element&lt;br /&gt;
* module&#039;s context: search for &amp;quot;callHooks(&amp;quot; without the quotes or take a look at the wiki: http://wiki.dolibarr.org/index.php/Hooks_system&lt;br /&gt;
or you can get it by printing it in the /htdocs/customfields/class/actions_customfields.class.php by adding print_r($parameters) and search for $parameters-&amp;gt;context&lt;br /&gt;
* module&#039;s trigger actions: search for &amp;quot;run_triggers(&amp;quot; or take a look at the wiki: http://wiki.dolibarr.org/index.php/Triggers#List_of_known_triggers_actions&lt;br /&gt;
* module&#039;s idvar: look at the url when you access one of the module&#039;s record, and you should see something like fiche.php?xxxid=12 - you need the xxxid (eg: socid, facid, etc.).&lt;br /&gt;
* module&#039;s security: in the page shown in URL when accessing the module&#039;s datasheet, edit this file and add &#039;&#039;&#039;print($user-&amp;gt;rights)&#039;&#039;&#039;, then in the list try to find the correct right (generally something like $user-&amp;gt;modulename-&amp;gt;creer). You can also try to search for $user-&amp;gt;rights in the sourcecode of this module.&lt;br /&gt;
&lt;br /&gt;
With these values, edit the config file (/htdocs/customfields/conf/conf_customfields.lib.php), particularly the $modulesarray and $triggersarray variables:&lt;br /&gt;
&lt;br /&gt;
=== Configure hooks and main parameters ===&lt;br /&gt;
&lt;br /&gt;
Add every parameters except triggers in the $modulesarray:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$modulesarray = array(&lt;br /&gt;
                      array(&#039;context&#039;=&amp;gt;&#039;invoicecard&#039;, &#039;table_element&#039;=&amp;gt;&#039;facture&#039;, &#039;idvar&#039;=&amp;gt;&#039;facid&#039;, &#039;rights&#039;=&amp;gt;array(&#039;$user-&amp;gt;rights-&amp;gt;facture-&amp;gt;creer&#039;)), // Client Invoice&lt;br /&gt;
                      array(&#039;context&#039;=&amp;gt;&#039;membercard&#039;, &#039;table_element&#039;=&amp;gt;&#039;adherent&#039;, &#039;idvar&#039;=&amp;gt;&#039;rowid&#039;, &#039;rights&#039;=&amp;gt;array(&#039;$user-&amp;gt;rights-&amp;gt;adherent-&amp;gt;creer&#039;), &#039;tabs&#039;=&amp;gt;array(&#039;objecttype&#039;=&amp;gt;&#039;member_admin&#039;, &#039;function&#039;=&amp;gt;&#039;member_admin_prepare_head&#039;, &#039;lib&#039;=&amp;gt;DOL_DOCUMENT_ROOT.&#039;/core/lib/member.lib.php&#039;)), // Members / Adherents&lt;br /&gt;
                     ); // Edit me to add the support of another module - NOTE: Lowercase only!&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are also a lot of others parameters you can give to CustomFields so that the module gets better managed or to add a few other functionnalities, here is a non-exhaustive list:&lt;br /&gt;
* context: name of the module&#039;s context.&lt;br /&gt;
* table_element: name of the sql table that holds the object&#039;s datas.&lt;br /&gt;
* idvar: name of the variable that holds the id of the object in the sql database (by default &#039;id&#039; or &#039;rowid&#039;).&lt;br /&gt;
* rights: prevent the custom fields to be editable unless all the rights set here are enabled (preventing custom fields to be viewed is useless because they won&#039;t show anyway if the page can&#039;t be loaded due to insufficient rights, so this is directly handled by the module).&lt;br /&gt;
* tabs=&amp;gt;array(objecttype, function, lib, tabname, tabtitle): used to embed CustomFields&#039;s admin panel into another module&#039;s admin page.&lt;br /&gt;
&lt;br /&gt;
=== Configure creation triggers ===&lt;br /&gt;
&lt;br /&gt;
Add the triggers in $triggersarray:&lt;br /&gt;
$triggersarray = array(&amp;quot;order_create&amp;quot;=&amp;gt;&amp;quot;commande&amp;quot;,&lt;br /&gt;
                       &amp;quot;yourmodule_triggeraction1&amp;quot;=&amp;gt;&amp;quot;yourmodule_tablename&amp;quot;,&lt;br /&gt;
                       &amp;quot;yourmodule_triggeraction2&amp;quot;=&amp;gt;&amp;quot;yourmodule_tablename&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
Note: generally you will be looking to implement the _CREATE.&lt;br /&gt;
&lt;br /&gt;
Note2: if a triggeraction is not recognized by CustomFields (eg: mymodule_addpayment) then by default CustomFields will assimilate it to a _create action (it will create and save the values for the custom fields, just for a module&#039;s creation page).&lt;br /&gt;
&lt;br /&gt;
Note3: CustomFields also supports a few other triggers like _CLONE, _UPDATE, _MODIFY, but they are not required and may not work depending on how your module works to manage these actions.&lt;br /&gt;
&lt;br /&gt;
=== Configuration troubleshooting ===&lt;br /&gt;
&lt;br /&gt;
* If you cannot see the custom fields appear on your module&#039;s page, this means the hooks are not implemented or configured in CustomFields, please read the related chapters.&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet but there&#039;s no edit button icon: the module implements security but you did not configure it properly in CustomFields, please find the right security in $user-&amp;gt;rights and set it in conf_customfields.lib.php.&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the creation page but are not saved: you forgot to set the trigger in $triggersarray, or the creation trigger does not exist for this module and you have to implement it. Jump to [[Module_CustomFields_Dev#Triggers]].&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet but not on the creation page (or vice versa): A hook is missing on the page where the custom fields do not appear. Jump to [[Module_CustomFields_Dev#Hooks]].&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet with the edit button, but when the edit button is clicked, everything disappear: either the $idvar is not properly set (look at the URL to know if you need to set idvar), or the hook is not properly made ($object is not defined and it&#039;s called by another name, but the hook use $object instead of the correct variable).&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet with the edit button, can be edited but cannot be saved (the data is not saved): same as the previous point: either $idvar or $object bug.&lt;br /&gt;
&lt;br /&gt;
Also you might find useful to &#039;&#039;&#039;[[Module_CustomFields#General_advices_for_troubleshooting|enable php errors]]&#039;&#039;&#039; to better track errors if that&#039;s not already done.&lt;br /&gt;
&lt;br /&gt;
= Example cases =&lt;br /&gt;
&lt;br /&gt;
You can find a compilation of concrete, practical examples on the [[Module_CustomFields_Cases]] wiki page.&lt;br /&gt;
&lt;br /&gt;
= TO DO =&lt;br /&gt;
&lt;br /&gt;
You will find below the public list of features that should be implemented in the future.&lt;br /&gt;
&lt;br /&gt;
If you have an idea about a new feature that isn&#039;t listed below, feel free to add it to the list!&lt;br /&gt;
&lt;br /&gt;
== To Do ==&lt;br /&gt;
Nothing here!&lt;br /&gt;
&lt;br /&gt;
== To Document ==&lt;br /&gt;
* Automatic recopy on cloning for custom fields and products&#039; lines custom fields.&lt;br /&gt;
* new API function: customfields_clone_or_recopy($object, $fromobject, $action2 = null)&lt;br /&gt;
&lt;br /&gt;
== Should do ==&lt;br /&gt;
* Add the possibility to mark a custom field to be shown in tables listings (eg: as a column in products listings) with &amp;quot;order by this column&amp;quot; buttons and stuffs. Will be difficult if the hooks are not yet implemented in listings.&lt;br /&gt;
* Checkbox (multiple checkboxes, one for each possible value) and multiselect (big select and can use SHIFT to multiselect) (store in XML file) (but impossible with constrained fields, can&#039;t store multiple foreign keys ids in one single sql field, but would work for other types like enum)&lt;br /&gt;
use fieldset, input checkbox and label for, eg:&lt;br /&gt;
    &amp;lt;fieldset&amp;gt;&amp;lt;legend&amp;gt;&amp;lt;label for=&amp;quot;cf_productslist&amp;quot;&amp;gt; $langs-&amp;gt;trans(&#039;cf_productslist&#039;)&amp;lt;/label&amp;gt;&amp;lt;/legend&amp;gt;&lt;br /&gt;
    &amp;lt;input type=&amp;quot;checkbox&amp;quot; name=&amp;quot;cf_productslist_choice1&amp;quot; value=&amp;quot;1&amp;quot; id=&amp;quot;cf_productslist_choice1&amp;quot; /&amp;gt;&amp;lt;label for=&amp;quot;cf_productslist_choice1&amp;quot;&amp;gt; $langs-&amp;gt;trans(&#039;cf_productslist_choice1&#039;)&amp;lt;/label&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
    &amp;lt;input type=&amp;quot;checkbox&amp;quot; name=&amp;quot;cf_productslist_choice2&amp;quot; value=&amp;quot;1&amp;quot; id=&amp;quot;cf_productslist_choice2&amp;quot; /&amp;gt;&amp;lt;label for=&amp;quot;cf_productslist_choice2&amp;quot;&amp;gt; $langs-&amp;gt;trans(&#039;cf_productslist_choice2&#039;)&amp;lt;/label&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
    etc...&lt;br /&gt;
    &amp;lt;/fieldset&amp;gt;&lt;br /&gt;
* Add a javascript options values generator for the enum type (a hidden input that would be shown only when DropdownBox type is selected and that would permit to add options by clicking a plus button).&lt;br /&gt;
* Replace triggers in customfields class by hookmanager?&lt;br /&gt;
* AJAX previsualisation of the HTML custom field that will be created.&lt;br /&gt;
* In ODTs, create special tags to directly access day, month or year value of a date type field independently of the rest.&lt;br /&gt;
* New overloading function&#039;s action: &amp;quot;get&amp;quot;, similar to view except that it works not only on Dolibarr&#039;s view but whenever someone tries to access this field&#039;s value.&lt;br /&gt;
* Add Upload and Image field types (this would allow for an easier integration inside ODT and PDF templates than the current workflow of Dolibarr).&lt;br /&gt;
&lt;br /&gt;
== Known bugs ==&lt;br /&gt;
* in product and service modules, if you edit a field, the proposals and other fields below won&#039;t be shown, you need to refresh the page. This problem resides in Dolibarr I think (since we are simply using a hook).&lt;br /&gt;
* ficheinter (Intervention module) custom fields will stay in create mode just after create page is submitted (this is because of the $action that is not standard - $action=edit or something).&lt;br /&gt;
&lt;br /&gt;
== Never/Maybe one day ==&lt;br /&gt;
* Add support for repeatable (predefined) invoices (the way it is currently managed makes it very difficult to manage this without making a big exception, adding specific functions in customfields modules that would not at all will be reusable anywhere else, when customfields has been designed to be as generic as possible to support any module and any version of dolibarr, because it&#039;s managed by a totally different table while it&#039;s still managed by the same module, CustomFields work with the paradigm: one module, one table).&lt;br /&gt;
* Add an AJAX select box for constrained values : when a constrained type is selected and a table is selected, a hidden select box would show up with the list of the fields of this table to choose the values that will be printed as the values for this customfield (eg: for table llx_users you could select the &amp;quot;nom&amp;quot; field and then it would automatically prepend &amp;quot;nom_&amp;quot; to the field&#039;s name).&lt;br /&gt;
* Fine-grained rights management: rights per group, per user, and per field.&lt;br /&gt;
* Replace overloading functions (extend class) by hookmanager if possible (but how to simulate the *full switch? Plus this will force users to make their own modules to make those functions, and to disable/renable their modules everytime they will add a new function...).&lt;br /&gt;
* Refactor the trigger array: merge it with the modulesarray (along with a new way to specify which customfields trigger action a trigger should correspond, eg: &#039;linebill_insert&#039;=&amp;gt;&#039;customfields_create&#039;). But the triggers aren&#039;t necessarily associated with a specific module...&lt;/div&gt;</summary>
		<author><name>Lrq3000</name></author>
	</entry>
	<entry>
		<id>https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Dev&amp;diff=33137</id>
		<title>Module CustomFields Dev</title>
		<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Dev&amp;diff=33137"/>
		<updated>2015-02-03T22:39:51Z</updated>

		<summary type="html">&lt;p&gt;Lrq3000: /* Printing in documents */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:CustomFields]]&lt;br /&gt;
{{TemplateDocDevEn}}&lt;br /&gt;
{{TemplateModEN}}&lt;br /&gt;
{{BasculeDevUserEn|&lt;br /&gt;
name=CustomFields|&lt;br /&gt;
num=8500|&lt;br /&gt;
devdoc=This page|&lt;br /&gt;
userdoc=[[Module_CustomFields]]|}}&lt;br /&gt;
[[Category:FAQ EN]]&lt;br /&gt;
&lt;br /&gt;
These notes are aimed to developpers who want to extend the functionnalities of the module or want to use it for extreme cases.&lt;br /&gt;
This is not meant to guide implementers on how to implement and configure the module (see the related chapter for that).&lt;br /&gt;
&lt;br /&gt;
If you are an implementer and just want to use CustomFields with its normal functions, you can read the [[Module_CustomFields|user documentation of CustomFields]].&lt;br /&gt;
&lt;br /&gt;
You can also jump directly to [[Module_CustomFields_Cases]] wiki page for a collection of practical examples.&lt;br /&gt;
&lt;br /&gt;
= Principles =&lt;br /&gt;
&lt;br /&gt;
CustomFields is simply a wrapper for SQL databases.&lt;br /&gt;
&lt;br /&gt;
The module is based on SQL to manage the fields and most of the stuff. It uses and understands standard SQL.&lt;br /&gt;
&lt;br /&gt;
The main principles of CustomFields are:&lt;br /&gt;
&lt;br /&gt;
* one SQL column = one Dolibarr field.&lt;br /&gt;
* one SQL type = one method to show and edit the field in Dolibarr.&lt;br /&gt;
* almost everything should be configurable from the configuration file, or admin interface.&lt;br /&gt;
* be independent from Dolibarr&#039;s data structures.&lt;br /&gt;
&lt;br /&gt;
CustomFields sits besides Dolibarr, and does not mix with it, even if in the Dolibarr&#039;s interface it looks like CustomFields is embedded inside Dolibarr, in reality, CustomFields is totally independent.&lt;br /&gt;
&lt;br /&gt;
= Goals =&lt;br /&gt;
&lt;br /&gt;
This module was made to provide an easy way to manage custom sql fields, not only for users and implementers, but also for developpers to use in their own module:&lt;br /&gt;
* by easily adding the support of custom fields for your own module by using this module;&lt;br /&gt;
* by reusing custom fields from another module in your own module;&lt;br /&gt;
* by managing sql fields in your own module by using the CustomFields class (which will take care of everything from printing, editing to PDF/ODT templating).&lt;br /&gt;
&lt;br /&gt;
So this module is not only an auxiliary of other modules, but it can also be the core part of your own module by using it as your SQL query class for fields.&lt;br /&gt;
&lt;br /&gt;
= SQL structure =&lt;br /&gt;
&lt;br /&gt;
A CustomFields table needs to respect three parameters that never change:&lt;br /&gt;
&lt;br /&gt;
* table&#039;s name (format: llx_*dolibarrmodule*_customfields where *dolibarrmodule* = table_element in conf_customfields.lib.php)&lt;br /&gt;
* first column: rowid, which is the primary key for each field&lt;br /&gt;
* second column: fk_*dolibarrmodule* which is the foreign key that links every customfields record to the Dolibarr&#039;s module that is associated (invoice, order, etc...).&lt;br /&gt;
&lt;br /&gt;
Apart from the table&#039;s name and the first two special columns, every other columns are just standard sql columns that you can freely create, edit or delete as you want, from the module&#039;s administration page or from a third-party SQL management tool (such as phpMyAdmin).&lt;br /&gt;
&lt;br /&gt;
You can also create more complex sql fields: constraints, foreign keys, transformations, other sql types, etc...).&lt;br /&gt;
&lt;br /&gt;
Then the module will try to manage the sql fields you manually created the best it can:&lt;br /&gt;
* by printing appropriate input fields in the Dolibarr gui interface.&lt;br /&gt;
* by providing the developper an easy access to these fields in php (eg: $customfields-&amp;gt;cf_myfield or $object-&amp;gt;customfields-&amp;gt;cf_myfield) for any use, including PDF templates and ODT templates, or in any php script (so that you can use these fields in your own module).&lt;br /&gt;
&lt;br /&gt;
= SQL Indexes =&lt;br /&gt;
&lt;br /&gt;
The module automatically adds an index on the primary key and foreign key (first and second columns).&lt;br /&gt;
&lt;br /&gt;
To further optimize the performances of your database, and particularly the customfields tables, you may want to manually add indexes based on usage.&lt;br /&gt;
&lt;br /&gt;
Also, you may add indexes on remote tables used by constrained fields, which could optimize even more the performances since constrained fields have the worst performance drawback for CustomFields.&lt;br /&gt;
&lt;br /&gt;
= SQL Compatibility mode =&lt;br /&gt;
&lt;br /&gt;
The module relies on referential integrity checks and foreign keys of your DBMS to keep the database clean and to provide the constrained fields feature. Referential integrity checks is part of the SQL standards and should be used whenever possible, which always was a main goal for CustomFields (to be respectful of SQL standards).&lt;br /&gt;
&lt;br /&gt;
Unluckily, a few DBMS systems still don&#039;t support this critical functionality of the SQL standard, for example MySQL enables MyISAM engine by default, which doesn&#039;t support this feature.&lt;br /&gt;
&lt;br /&gt;
In these cases, you can either do two things:&lt;br /&gt;
* Switch to an engine which support referential integrity. For example with MySQL, you can enable InnoDB, which support referential integrity checks and foreign keys.&lt;br /&gt;
* Use CustomFields compatibility mode (more infos below).&lt;br /&gt;
&lt;br /&gt;
In the case your database does not support referential integrity checks, CustomFields will automatically switch to the SQL compatibility mode, which will allow you to use CustomFields even on databases where foreign keys are not possible.&lt;br /&gt;
&lt;br /&gt;
In compatibility mode, you will get nearly all CustomFields advanced features such as Constrained fields, but the customfields&#039; tables in the database won&#039;t be cleaned automatically (no delete on cascade, meaning that when you delete an object, the linked custom fields will still be kept in your database), and constrained fields won&#039;t be updated automatically on update.&lt;br /&gt;
&lt;br /&gt;
Nevertheless, these missing cleanup functionalities won&#039;t be seen by your users because they will never be accessible: on deletion of an object, the custom fields won&#039;t ever be accessible again from Dolibarr&#039;s interface, and object&#039;s ids are never changed since Dolibarr also relies on this. Thus in compatibility mode, CustomFields will act just like normally, but your database will be a bit less clean.&lt;br /&gt;
&lt;br /&gt;
= JSON extension for extra options =&lt;br /&gt;
&lt;br /&gt;
== Introduction to JSON in CustomFields ==&lt;br /&gt;
The relational model is very great to store and fetch data with a declarative language, and this is very powerful, but this model also imposes a few limitations, for example the relations must be well formatted and predefined.&lt;br /&gt;
&lt;br /&gt;
That&#039;s why we finally implemented a &#039;&#039;&#039;JSON extension&#039;&#039;&#039; to manage &#039;&#039;&#039;extra options&#039;&#039;&#039; and associate them with custom fields. This allows to add many properties (or columns if you are more used to the relational model) on-the-fly without having to predefine them prior to storing and using them.&lt;br /&gt;
&lt;br /&gt;
This allows to add an undefined number of columns/properties/extra options that will help manage special behaviours for custom fields. For example, it is currently used to allow to change the order of appearance of custom fields, and to store and manage remote table and columns datas for constrained fields on DBMS that do not support Foreign Keys checks.&lt;br /&gt;
&lt;br /&gt;
We chose JSON over XML because the library are simpler and the data footprint smaller, and because CustomFields does not need advanced features of XML like XQuery, data typing and meta structures (at least not for now).&lt;br /&gt;
&lt;br /&gt;
The other advantage is that php objects can directly be encoded into JSON without any preprocessing, and can then as straightly be decoded from JSON back into a php object.&lt;br /&gt;
&lt;br /&gt;
A simple mean to encode and decode back an object into and from a storage place, as well as allow users to easily add/remove extra options were the main requirement, and JSON (+ JSON libraries for PHP) meet this criteria.&lt;br /&gt;
&lt;br /&gt;
However, note that the extra options are now encoded as an associative array, so as to avoid any ambiguities (there&#039;s no associative array in JSON, they are converted to objects).&lt;br /&gt;
&lt;br /&gt;
== Implementation of JSON ==&lt;br /&gt;
The implementation is very simple: a table called &#039;&#039;&#039;llx_customfields_extraoptions&#039;&#039;&#039; is created when you enable the CustomFields module, and this table only contains three columns:&lt;br /&gt;
* table_name (varchar)&lt;br /&gt;
* column_name (varchar)&lt;br /&gt;
* extraoptions (blob)&lt;br /&gt;
&lt;br /&gt;
(table_name, column_name) both forms the primary key of the table, and links to columns (custom fields) of other customfields tables.&lt;br /&gt;
extraoptions is a blob, which allows to store the data as binary (as-is) without encoding (which solves a big problem of JSON which is encoding special and foreign characters, here there is no problem at all).&lt;br /&gt;
&lt;br /&gt;
When fetching custom fields, the module simply JOIN the extraoptions table, which adds an &#039;&#039;&#039;extraoptions&#039;&#039;&#039; column for each custom field in the SQL result. Then it is quite easy to just json_decode() the data returned inside the extraoptions column.&lt;br /&gt;
&lt;br /&gt;
This methodology allows to have all the advantages of a relational database, with the flexibility of a free-schema model:&lt;br /&gt;
* everything is stored inside the database (only one storage place)&lt;br /&gt;
* fixed columns allows for relational joining and optimization&lt;br /&gt;
* thus queries (fetching) are optimised&lt;br /&gt;
* at the same time, the extraoptions are automatically retrieved and associated with every custom field, and whenever needed they can be used&lt;br /&gt;
* and thus we also get a flexible model to &#039;&#039;&#039;add extra (virtual) columns whenever needed&#039;&#039;&#039; without having to alter the relational database schema&lt;br /&gt;
&lt;br /&gt;
Thus no critical information is and should be stored inside the extraoptions column because they would not be query-able, only extra options that are just used to tweak the management of those fields inside the PHP application should be stored. But this is a wonderful way to add non critical informations that still allows to develop more complex features and management for special custom fields.&lt;br /&gt;
&lt;br /&gt;
== CustomFields JSON API ==&lt;br /&gt;
&lt;br /&gt;
=== Storing extra options ===&lt;br /&gt;
&lt;br /&gt;
You can use the &#039;&#039;&#039;setExtra()&#039;&#039;&#039; method to set extra options of a custom field.&lt;br /&gt;
&lt;br /&gt;
Eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$extra = array(); // $extra should always be an associative array, it&#039;s no longer an object&lt;br /&gt;
$extra[&#039;myproperty&#039;] = &#039;Anything I want here&#039;; // set a property/extra option like this&lt;br /&gt;
$extra[&#039;category&#039;][&#039;subcategory&#039;][&#039;prop&#039;] = true; // we can set recursive properties, there&#039;s not any limit to how deep the hierarchy can be&lt;br /&gt;
&lt;br /&gt;
require_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomField($db, &#039;currentmodule&#039;);&lt;br /&gt;
$fieldid = &#039;mycustomfields&#039;; // $fieldid can either be a column_name or an ordinal_position from information_schema.columns&lt;br /&gt;
$result = $customfields-&amp;gt;setExtra($fieldid, $extra); // store the extra options inside the database&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can also use addCustomField() or updateCustomField() with an $extra array, and these functions will also call the setExtra() method.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: the extra options you set are &#039;&#039;&#039;appended&#039;&#039;&#039; to the ones already stored in the database. However, you can also overwrite previous options, just use the same name of property but with a different value (the replacement is recursive, you can do it at any level in the hierarchy).&lt;br /&gt;
&lt;br /&gt;
=== Fetching extra options ===&lt;br /&gt;
&lt;br /&gt;
Extra options are automatically fetched along with a custom field structure, decoded, and stored inside the extra sub-property of any field object, or CustomFields object.&lt;br /&gt;
&lt;br /&gt;
When fetched, extra options are stored as a sub-object for the current field object (which itself is stored as a subobject of the CustomFields object if caching is enabled).&lt;br /&gt;
&lt;br /&gt;
Eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
require_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomField($db, &#039;currentmodule&#039;);&lt;br /&gt;
$fields = fetchAllFieldsStruct(); // fetching fields structure will automatically fetch extra options at the same time. This works with fetchAllFieldsStruct() and fetchFieldStruct()&lt;br /&gt;
&lt;br /&gt;
// Now that you fetched the fields structure, you can just access the extra options sub-properties:&lt;br /&gt;
print_r($fields-&amp;gt;mycustomfield-&amp;gt;extra); // will print all extra options for the field mycustomfield&lt;br /&gt;
print_r($customfields-&amp;gt;fields-&amp;gt;mycustomfield-&amp;gt;extra); // you can also access the same field(s) directly from the CustomFields object if caching is enabled (default behaviour)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Modifying extra options ===&lt;br /&gt;
&lt;br /&gt;
You can just combine the two previous points to edit extra options of a custom field: just load a custom field, edit on-the-fly the extra property with PHP code, and then store it back directly into the database:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
require_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomField($db, &#039;currentmodule&#039;);&lt;br /&gt;
$fields = fetchAllFieldsStruct();&lt;br /&gt;
&lt;br /&gt;
$fields-&amp;gt;mycustomfield-&amp;gt;extra[&#039;newproperty&#039;] = &#039;This is a new property&#039;; // add a new property over the old extra options sub-object&lt;br /&gt;
$fields-&amp;gt;mycustomfield-&amp;gt;extra[&#039;mycategory&#039;] = &#039;Overwrite the old category&#039;; // this will replace the previous value of &#039;mycategory&#039; extra option&lt;br /&gt;
&lt;br /&gt;
$customfields-&amp;gt;setExtra(&#039;mycustomfield&#039;, $fields-&amp;gt;mycustomfield-&amp;gt;extra); // store the modified extra options back into the database for future usage&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= CustomFields API =&lt;br /&gt;
&lt;br /&gt;
== Introduction to CustomFields API ==&lt;br /&gt;
&lt;br /&gt;
This chapter will cover the basics on how to manually and concretely use the CustomFields class to create/update/fetch/manage custom fields.&lt;br /&gt;
&lt;br /&gt;
This will give you a better understanding of how CustomFields works, as well as giving you insights on how to use it for your own applications.&lt;br /&gt;
&lt;br /&gt;
Important note: contrary to the Facade API, the core CustomFields class use columns names without any prefix, so don&#039;t try to use the prefix, it won&#039;t work! For example: if your field is named &#039;mycustomfield&#039; and prefix is &#039;cf_&#039;, when you use the Facade API you should use &#039;$object-&amp;gt;$path-&amp;gt;cf_mycustomfield&#039; but when you use the core CustomFields class directly, $fieldname = &#039;mycustomfield&#039;.&lt;br /&gt;
&lt;br /&gt;
== Prerequisite to use the CustomFields class ==&lt;br /&gt;
&lt;br /&gt;
Before using the CustomFields class, you first must instanciate it. This is a necessary preliminary step before using any of the methods that will be covered inside this chapter, and thus will not be repeated, only shown once here.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Include the CustomFields class (which contains the main API)&lt;br /&gt;
require_once(dirname(__FILE__).&#039;/../class/customfields.class.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Instanciate a CustomFields object&lt;br /&gt;
global $db; // the database handler created by Dolibarr&lt;br /&gt;
$table_element = &#039;currentmodule&#039;; // where $table_element is the database&#039;s table name for the module, without the Dolibarr prefix (eg: for &#039;llx_facture&#039;, table_element is &#039;facture&#039;). You can also find it inside the $object properties (just print_r() it).&lt;br /&gt;
$customfields = new CustomFields($db, $table_element); // instanciate the CustomFields class inside $customfields object&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
CustomFields keeps track of errors that can happen when executing methods and SQL queries inside the &#039;&#039;&#039;error&#039;&#039;&#039; and &#039;&#039;&#039;errors&#039;&#039;&#039; properties, which you can print with the printErrors() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Do something with CustomFields before&lt;br /&gt;
&lt;br /&gt;
if (count($customfields-&amp;gt;errors) &amp;gt; 0) { // If there is at least one error&lt;br /&gt;
    $customfields-&amp;gt;printErrors(); // print errors&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// You can also print your own error message, in two manners:&lt;br /&gt;
&lt;br /&gt;
// 1- submit your own message&lt;br /&gt;
$customfields-&amp;gt;printErrors(&#039;Your own error message here&#039;); // this will print ONLY your error message, using the Dolibarr&#039;s AJAX error dialog (standard way of printing errors)&lt;br /&gt;
&lt;br /&gt;
// 2- append your message in the customfields errors&lt;br /&gt;
$customfields-&amp;gt;errors[] = &#039;Your own error message to be appended with other CustomFields messages&#039;;&lt;br /&gt;
$customfields-&amp;gt;printErrors(); // Here your error message will be appended, thus all error messages will be printed. This allows you for example to add more intelligible informations on a specific error.&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== General purpose SQL methods ==&lt;br /&gt;
&lt;br /&gt;
CustomFields provide an interface to easily execute various common SQL requests, as well as your own custom SQL requests.&lt;br /&gt;
&lt;br /&gt;
* fetchAllTables() fetch a list of all the tables in the database&lt;br /&gt;
* fetchPrimaryField($table) fetch the column_name (and only the column_name!) of the primary field of a table. You can then use fetchFieldStruct()  with the column_name to get any information you want.&lt;br /&gt;
* fetchAny($columns, $table, $where=&#039;&#039;, $orderby=&#039;&#039;, $limitby=&#039;&#039;) allows you to issue any simple SQL query command, for example fetchAny(&#039;*&#039;, &#039;sometable&#039;) is equivalent to SELECT * FROM sometable; (but then everything is managed automatically and result is returned to you)&lt;br /&gt;
* executeSQL($sql, $eventname) to execute any SQL query you want, this will return you a resource (and NOT a processed nuplet like fetchAny!). $eventname can be anything, it&#039;s just for Dolibarr logging facility.&lt;br /&gt;
* executeMultiSQL($sql, $eventname) to execute multiple SQL queries&lt;br /&gt;
* fetchFieldStruct($id=null, $nohide=false, $table=null, $cachepath=null) allows you to fetch the field&#039;s SQL structure of not only custom fields, but of any database&#039;s column using the $table variable. Eg: fetchFieldStruct(&#039;mycolumn&#039;, true, &#039;mytable&#039;);&lt;br /&gt;
* probeTable($table) helps you to check if a table exists in the database&lt;br /&gt;
* fetch_object($resource) will return a PHP object from a $resource, similarly to mysql_fetch_object() but with two advantages: it works on several DBMS (not only mysql), and it always return lowercase properties names (else depending on the configuration of the DBMS, the request can return uppercase or mixed case properties names).&lt;br /&gt;
* escape() and reverse_escape() are similare to mysql_real_escape but is reversible and DBMS-independent (so you can use it with other DBMSes).&lt;br /&gt;
&lt;br /&gt;
== Managing CustomFields structure ==&lt;br /&gt;
&lt;br /&gt;
The first step into CustomFields is to create and manage the structure of a custom field.&lt;br /&gt;
&lt;br /&gt;
=== Creating a custom field definition ===&lt;br /&gt;
&lt;br /&gt;
To create a custom field, you can use the addCustomField() method.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;addCustomField($fieldname, $type, $size, $nulloption, $defaultvalue = null, $constraint = null, $customtype = null, $customdef = null, $customsql = null, $fieldid = null, $extra = null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will take care of forging the (complicated) SQL query with the forgeSQLCustomField() method, and will return you an error if there was a problem.&lt;br /&gt;
&lt;br /&gt;
=== Updating a custom field definition ===&lt;br /&gt;
&lt;br /&gt;
To update a custom field, you can use the updateCustomField() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;&lt;br /&gt;
updateCustomField($fieldid, $fieldname, $type, $size, $nulloption, $defaultvalue, $constraint = null, $customtype = null, $customdef = null, $customsql = null, $extra = null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: the updateCustomField() and addCustomField() methods can both be used since updateCustomField() is in fact an alias for addCustomField(). CustomFields will automatically detect if a custom field already exists, and update it if that&#039;s true, or create it.&lt;br /&gt;
&lt;br /&gt;
Note2: updating a custom field will automatically take care of the constraints and foreign keys on its own, thus you can change the foreign key, or remove it altogether if set to null.&lt;br /&gt;
&lt;br /&gt;
=== Delete a custom field definition ===&lt;br /&gt;
&lt;br /&gt;
You can easily delete a custom field by column_name or by ordinal_position:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;deleteCustomField($id);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: constraints and foreign keys will also be deleted automatically so you shouldn&#039;t encounter any error.&lt;br /&gt;
&lt;br /&gt;
=== Fetch one custom field&#039;s SQL structure ===&lt;br /&gt;
&lt;br /&gt;
To get the SQL structure of a custom field, which can be quite useful to know for example its datatype or if it has a foreign key, or accepts null values, you can use the fetchFieldStruct() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
fields = $customfields-&amp;gt;fetchFieldStruct($id=null, $nohide=false, $table=null, $cachepath=null); // where $id can be a column_name, an ordinal_position from information_schema.columns or null if you want to fetch all the columns at once&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method will either return null, either a single field object, either an array on field objects.&lt;br /&gt;
&lt;br /&gt;
Note: this function will use caching by default to accelerate future results, but you can disable it, or even change the default cache path. For more informations please read the comments in the source code.&lt;br /&gt;
&lt;br /&gt;
=== Fetch several custom fields&#039; SQL structures ===&lt;br /&gt;
&lt;br /&gt;
Use the fetchAllFieldsStruct() method, which is an alias for fetchFieldStruct():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;fetchAllFieldsStruct($nohide=false, $table=null, $cachepath=null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: this method will always return an array of fields objects, even if only one is found (for more consistency), and this is the main difference with fetchFieldStruct(null).&lt;br /&gt;
&lt;br /&gt;
== Managing custom fields records ==&lt;br /&gt;
&lt;br /&gt;
Now that you know how to create and manage the structure of custom fields, we can proceed on to managing the records.&lt;br /&gt;
&lt;br /&gt;
=== Creating a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Simply use the create() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;create($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
$object must be an object containing as its properties the customfields data.&lt;br /&gt;
&lt;br /&gt;
For example, if you have a field named mycustomfield, your object must have $object-&amp;gt;mycustomfields defined.&lt;br /&gt;
&lt;br /&gt;
This method will store in the database ALL the custom fields that were created/edited and with a valid value (eg: an empty value for a field that does not accept null value is invalid) AT ONCE! So you can modify all the columns of one record at once, but not several records at once.&lt;br /&gt;
&lt;br /&gt;
Concretely, this is very useful with Dolibarr, because modules tends to store everything inside a single object generally called $object, and CustomFields tries to hook onto $object, so that you can just pass $object as is to create() and it will save all the custom fields.&lt;br /&gt;
&lt;br /&gt;
But you can also create an empty object with $object = new stdClass(); and then manually fill the custom fields records with whatever you want, and then call the create() method, this will work as well!&lt;br /&gt;
&lt;br /&gt;
You can also just modify only one column of one record, by just specifying one custom field column_name in your $object. The other custom fields that are not specified won&#039;t be created/modified.&lt;br /&gt;
&lt;br /&gt;
=== Updating a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Use the update() method, which is an alias for the create() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;update($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As noted above, update() is an alias of the create() method, which means that CustomFields will try to detect by itself if a record needs to be updated or created.&lt;br /&gt;
&lt;br /&gt;
=== Deleting a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Use the delete() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;delete($id); // where $id is = to fk_moduleid column, NOT rowid&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fetching a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Just use the fetch() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$records = $customfields-&amp;gt;fetch($id=null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where $id can either be = fk_moduleid (NOT rowid), or null to fetch all the records at once.&lt;br /&gt;
&lt;br /&gt;
This method will either return null, a single record object, or an array of records objects.&lt;br /&gt;
&lt;br /&gt;
=== Fetching all custom fields&#039; records ===&lt;br /&gt;
&lt;br /&gt;
Use the fetchAll() method, which is an alias for fetch():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$records = $customfields-&amp;gt;fetchAll();&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: this method will always return an array of records object, for more consistency (and this is the main difference with fetch(null) ).&lt;br /&gt;
&lt;br /&gt;
=== Fetching referenced records of a constrained field ===&lt;br /&gt;
&lt;br /&gt;
A constrained custom field points to another database table, and a value for this constrained field is the rowid of a record in the referenced table.&lt;br /&gt;
&lt;br /&gt;
Thus, the principal purpose of a constrained custom field is not the value of the field in itself (it&#039;s simply an integer), but rather the other fields available from the referenced table, the constrained field being in fact just a pointer.&lt;br /&gt;
&lt;br /&gt;
To fetch the remote record from the referenced table, use the fetchReferencedValuesList() of the CustomFields class:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$fkrecord = fetchReferencedValuesList($field, $id=null, $where=null, $allcolumns=false);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
&lt;br /&gt;
* $field is a custom field object (fetched using fetchFieldStruct() or fetchAllFieldsStruct()),&lt;br /&gt;
* $id is the rowid of the record to get, just like fetch() and fetchAll(). Can be null to fetch all remote records (or to use the $where clause).&lt;br /&gt;
* $where is a string without the WHERE clause (eg: &amp;quot;fk_facture=1&amp;quot;) to get finer control. In this case, you can use $id or set $id=null depending on your purpose. This is mainly a private argument to allow for cascade option.&lt;br /&gt;
* $allcolumns if false, it will fetch only the required columns to do the smart value substitution. If true, it will fetch all remote columns (useful for ODT substitution). If an array of strings, each string will be a column to fetch from the remote database.&lt;br /&gt;
&lt;br /&gt;
Note that this function is NOT recursive (it will fetch the records from the referenced table, and that&#039;s all, it won&#039;t fetch the custom fields of the referenced table nor recursively fetch the remote constrained custom fields). To do recursive fetching, see in the Facade API.&lt;br /&gt;
&lt;br /&gt;
Note2: this function automatically manages Smart Value Substitution depending of the $field-&amp;gt;column_name.&lt;br /&gt;
&lt;br /&gt;
=== Recursive fetching referenced records of a constrained field ===&lt;br /&gt;
&lt;br /&gt;
If you want to recursively fetch a constrained field (eg: constrainedfield1-&amp;gt;ref_table-&amp;gt;constrainedfield2-&amp;gt;ref_table2-&amp;gt;...), you need another function called fetchReferencedValuesRec(). Technically, this function is in the Facade API (inside /customfields/lib/customfields_aux.lib.php), but it isn&#039;t meant to be used by lambda users (contrary to customfields_fill_object()), but is mainly a helper function for the rest of the facade API.&lt;br /&gt;
&lt;br /&gt;
fetchReferencedValuesRec() works very similarly to fetchReferencedValuesList(), but it will work recursively.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$fkrecord = fetchReferencedValuesRec($customfields, $field, $id, $recursive=true)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where $customfields is the CustomFields instance for the module you are working on, $field is the constrained field you want to fetch, $id is the record&#039;s id you want to fetch, and $recursive enables or disables the recursion.&lt;br /&gt;
&lt;br /&gt;
Note that this function automatically manages Smart Value Substitution (and also recursively).&lt;br /&gt;
&lt;br /&gt;
Note2: This function tries to automatically avoid an infinite recursion loop by using a $blacklist: for a given constrained custom field, a table won&#039;t be visited twice. This means that for example you can do: invoice-&amp;gt;user-&amp;gt;socpeople-&amp;gt;invoice, this will work and every referenced fields will be fetched, but it will automatically stop the recursion at the second invoice, because else it could continue in a loop to user, socpeople, invoice, user, socpeople, etc.&lt;br /&gt;
&lt;br /&gt;
Note3: when adding recursively referenced fields, the key in the returned array will be prefixed by the name of the custom field that led to those remote fields. Thus, you can have something like &amp;quot;cf_myfield1_cf_remotefield1_cf_remotefield2&amp;quot; if you have a constrained field cf_myfield1-&amp;gt;cf_remotefield1-&amp;gt;cf_remotefield2.&lt;br /&gt;
&lt;br /&gt;
== Input forms for custom fields ==&lt;br /&gt;
&lt;br /&gt;
The API provides some neat ways to automatically manage the input forms for users to input new values, with the best adapted HTML form based on the custom field&#039;s data type.&lt;br /&gt;
&lt;br /&gt;
The following methods will print input fields that are best adapted to the SQL data type of your custom fields as well as figure out the possible values and even format them (for example: a not null enum will be printed as a HTML select list without an empty option; fetch automatically remote columns for constrained fields and show them in a select).&lt;br /&gt;
&lt;br /&gt;
So you don&#039;t have to bother how your data will be presented to the user nor how it will be processed.&lt;br /&gt;
&lt;br /&gt;
=== Printing an input form adapted to the field&#039;s data type ===&lt;br /&gt;
&lt;br /&gt;
showInputField() is a sort of converter customfieldstructure -&amp;gt; HTML input field.&lt;br /&gt;
&lt;br /&gt;
Use the &#039;&#039;&#039;showInputField()&#039;&#039;&#039; method to print the right input form for a custom field.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;showInputField($field, $currentvalue=null, $moreparam=&#039;&#039;, $ajax_php_callback=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
showInputField() takes 4 parameters:&lt;br /&gt;
&lt;br /&gt;
* $field is a CustomFields structure object (eg: returned by fetchFieldStruct()) and is necessary to detect the right datatype and the possible values.&lt;br /&gt;
* $currentvalue is the current value if you want to preselect a value (for example if the user already defined a value, you can reload it here). This is optional, in case it is not defined, either a null/empty value will be chosen, or either the default value defined when creating the custom field.&lt;br /&gt;
* $moreparam is used to add more HTML attributes, if you want for example to add some css styling or whatever you want.&lt;br /&gt;
* $ajax_php_callback is optional and allows to specify the relative url to the php callback script. If specified, an AJAX script will be automatically attached to this field (see AJAX-PHP callback below).&lt;br /&gt;
&lt;br /&gt;
showInputField() only prints ONE HTML input field, thus you have to enclose it/them by your own HTML form.&lt;br /&gt;
&lt;br /&gt;
eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print &#039;&amp;lt;form method=&amp;quot;get&amp;quot; action=&amp;quot;?&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
print &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;customtoken&amp;quot; value=&amp;quot;ABCDEFGH&amp;quot; /&amp;gt;&#039;; // example of a custom hidden HTML input field, that can be used to pass a token for example&lt;br /&gt;
// Loop over each custom fields (you have to load them before with $customfields-&amp;gt;fetchAllFieldsStruct()&lt;br /&gt;
foreach ($customfields-&amp;gt;fields as $field) {&lt;br /&gt;
    print $customfields-&amp;gt;showInputField($field, $currentvalue=null, $moreparam=&#039;&#039;);&lt;br /&gt;
}&lt;br /&gt;
print &#039;&amp;lt;/form&amp;gt;&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can also store the resulting HTML input field and process it by a regexp for example:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$myhtmlfield = $customfields-&amp;gt;showInputField($field, $currentvalue=null, $moreparam=&#039;&#039;);&lt;br /&gt;
preg_replace($myhtmlfield, ..., ...);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: for constrained fields, if the name follows the convention of &#039;&#039;&#039;Smart Value Substitution&#039;&#039;&#039; (ie: remote columns names are included in the custom field&#039;s name), showInputField() will &#039;&#039;&#039;automatically fetch the remote values, format them, and present them&#039;&#039;&#039; in a HTML select.&lt;br /&gt;
&lt;br /&gt;
=== Printing an input form for a custom field ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;showInputForm()&#039;&#039;&#039; is a function used to print a single (and only one!) custom field with an adapted HTML input field, but enclosed by a HTML form. This is used for example when you want to allow to edit a single custom field but not the others.&lt;br /&gt;
&lt;br /&gt;
showInputForm() is in fact a wrapper for showInputField(), thus it will call showInputField() and simply enclose it by a form.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;showInputForm($id, $field, $currentvalue=null, $idvar=&#039;id&#039;, $page=null, $moreparam=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== AJAX-PHP callback ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;showInputFieldAjax()&#039;&#039;&#039; will output an AJAX script (using jQuery) to send and receive data to and from a specified php script. The AJAX script will automatically manage HTML form input&#039;s updating as long as the received data follows some convention.&lt;br /&gt;
&lt;br /&gt;
An AJAX script can be attached to any field (including non-CustomFields) by using the method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;showInputFieldAjax($id, $php_callback_url, $on_func=&amp;quot;change&amp;quot;, $request_type=&amp;quot;post&amp;quot;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
* $id is the HTML id of the field you want to attach the AJAX to.&lt;br /&gt;
* $phpcallback the relative path from Dolibarr&#039;s htdocs root folder to the php script that will receive and send back data.&lt;br /&gt;
* $on_func is the Javascript or jQuery event that should trigger the AJAX script (by default on change).&lt;br /&gt;
* $request_type is the HTML query type that will be used to send the data to the PHP callback (either &amp;quot;get&amp;quot; or &amp;quot;post&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
The AJAX script will automatically manage sending/receiving of data:&lt;br /&gt;
&lt;br /&gt;
* automatically send all form&#039;s inputs values, as well as the current calling field&#039;s name and value. The sending happens on change by default, but another event can be set using the $on_event argument. Data will be sent using standard GET or POST.&lt;br /&gt;
* Upon reception of data from the php script, the AJAX script will automatically parse the data and update the HTML fields.&lt;br /&gt;
&lt;br /&gt;
The PHP callback script must send back to AJAX the data in JSON encoded format, and with with a specific format:&lt;br /&gt;
&lt;br /&gt;
* the data must be an associative array, where each entry&#039;s key is the name (not ID!) of an HTML field to update.&lt;br /&gt;
* each entry&#039;s value is an associative array, where each sub-entry&#039;s key is the action to do. Available actions: &#039;options&#039;, &#039;value&#039;, &#039;html&#039;, &#039;alert&#039;.&lt;br /&gt;
* each sub-entry&#039;s value is the value for this action and HTML field.&lt;br /&gt;
&lt;br /&gt;
Thus, this approach allows to generically support a wide range of updating actions, managed automatically.&lt;br /&gt;
&lt;br /&gt;
You, the implementer, have just to edit the PHP callback script and return an associative array defining what action you want for each field you want to update. This is exactly what has been implemented in the Custom AJAX Functions library, which simplifies even more your job by managing automatically the step described here (generating AJAX and formatting data), so that you can focus on &#039;&#039;&#039;making the data&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Printing custom fields in documents ==&lt;br /&gt;
&lt;br /&gt;
The API can also automatically manage the printing and formatting of custom fields in your ODT, PDF and HTML documents.&lt;br /&gt;
&lt;br /&gt;
=== Printing in HTML ===&lt;br /&gt;
&lt;br /&gt;
You can either just print the value as-is, or pass them through the printField() or simpleprintField() function to format them (very useful for constrained fields) - see below.&lt;br /&gt;
&lt;br /&gt;
=== Printing in documents ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;printField()&#039;&#039;&#039; is used to properly print and translate a custom field&#039;s record&#039;s value. Generally, you can just print the custom field&#039;s value as-is, but in a few cases (eg for boolean types, or constrained fields), using the printField() method will more properly print the value.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;printField($field, $value, $outputlangs=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
* $field is the custom field&#039;s SQL structure as an object (eg: returned by fetchFieldStruct()).&lt;br /&gt;
* $value is the custom field&#039;s value for the current record (eg: $customfields-&amp;gt;fields-&amp;gt;myfield).&lt;br /&gt;
* $outputlangs is the Translate object for the target language.&lt;br /&gt;
&lt;br /&gt;
For example, with a constrained field with Smart Value Substitution, instead of printing the id like 1, it will print the full name of user like FirstName Name.&lt;br /&gt;
&lt;br /&gt;
This method will also translate a value if a translation is found in the language files.&lt;br /&gt;
&lt;br /&gt;
Another method &#039;&#039;&#039;printFieldPDF()&#039;&#039;&#039; can be used to properly print a value for a PDF (properly encoding the characters).&lt;br /&gt;
&lt;br /&gt;
Powertip: this function can also be used to get the Smart Value Substituted value of the field (instead of printing the rowid) in the field is constrained.&lt;br /&gt;
&lt;br /&gt;
=== Simple printing in documents ===&lt;br /&gt;
&lt;br /&gt;
If you don&#039;t want to have to manually fetch the structure of every custom field whose you want to print the value, you can then use a simplifier method called &#039;&#039;&#039;simpleprintField()&#039;&#039;&#039; which will take as input a custom field name instead of a custom field SQL structure object.&lt;br /&gt;
&lt;br /&gt;
This method is just a simplifier (Facade) wrapper for the printField() method, and thus will have the same advantages (fetch constrained fields, values translation, etc.).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;simpleprintField($fieldname, $value, $outputlangs=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
* $fieldname is the custom field&#039;s name (eg: &amp;quot;myfield&amp;quot;).&lt;br /&gt;
* $value is the custom field&#039;s value for the current record (eg: $customfields-&amp;gt;fields-&amp;gt;myfield).&lt;br /&gt;
* $outputlangs is the Translate object for the target language.&lt;br /&gt;
&lt;br /&gt;
Another method &#039;&#039;&#039;simpleprintFieldPDF()&#039;&#039;&#039; can be used to properly print a value for a PDF (properly encoding the characters).&lt;br /&gt;
&lt;br /&gt;
Powertip: this function can also be used to get the Smart Value Substituted value of the field (instead of printing the rowid) in the field is constrained.&lt;br /&gt;
&lt;br /&gt;
=== Find the label ===&lt;br /&gt;
&lt;br /&gt;
Finding the label of a custom field might not be as simple as it seems. First, values of properties objects may be easily used, but their name (key) is a worst job. Secondly, custom fields&#039; labels can be translated, and the following functions are expressly made for that purpose. Thirdly, for PDF and some other documents you have to encode the characters if you want them to properly be drawn.&lt;br /&gt;
&lt;br /&gt;
So if you want to print a custom field&#039;s label with translation (multilingual) support, just use the &#039;&#039;&#039;findLabel()&#039;&#039;&#039; method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$translatedfieldname = $customfields-&amp;gt;findLabel($fieldname, $outputlangs = &#039;&#039;); // where $fieldname is just a custom field label (eg: cf_mycustomfield or mycustomfield, both will work) and $outputlangs is the target language object (see the Translate class native of Dolibarr)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As for the other printing methods, you also have the &#039;&#039;&#039;findLabelPDF()&#039;&#039;&#039; wrapper method to easily print labels that with characters encoded for PDF.&lt;br /&gt;
&lt;br /&gt;
= Facade API =&lt;br /&gt;
&lt;br /&gt;
The Facade API was made to ease the usage of the CustomFields class with the Dolibarr system. It provides a neat way to do many operations with single commands, and will automatically manage the loading of the CustomFields class, and its usage with many optimizations.&lt;br /&gt;
&lt;br /&gt;
== customfields_fill_object() ==&lt;br /&gt;
&lt;br /&gt;
You can load the Facade API and then use the &#039;&#039;&#039;customfields_fill_object()&#039;&#039;&#039; to automatically fetch the fields structures and the records, and also automatically link to remote fields when a constrained field is found. This is an all-in-one solution to easily fetch CustomFields datas, and is also optimized to fully use caching and other special properties of the CustomFields class.&lt;br /&gt;
&lt;br /&gt;
Concretely, you just have to include the Facade API, then pass the customfields_fill_object() with a Dolibarr $object, and the Facade API will return a CustomFields object with appended the custom fields records and also the custom fields&#039; SQL structures.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$customfields = customfields_fill_object($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* $object will then include the custom fields records associated with this object (eg: $object-&amp;gt;customfields-&amp;gt;cf_yourfield).&lt;br /&gt;
* $customfields will be a CustomFields object containing the fields structures (eg: $customfields-&amp;gt;fields-&amp;gt;yourfield).&lt;br /&gt;
&lt;br /&gt;
You can also call any CustomFields function if you need to using the $customfields object, eg: $customfields-&amp;gt;simpleprintField($object-&amp;gt;customfields-&amp;gt;cf_yourfield);&lt;br /&gt;
&lt;br /&gt;
Note: $object can also be a dummy object containing only two properties that are required: $object-&amp;gt;id and $object-&amp;gt;table_element.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will now describe the full specification of this function:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$customfields = customfields_fill_object(&amp;amp;$object,$fromobject = null, $outputlangs = null, $prefix = null,$pdfformat = false);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* $object is the object where the customfields will be saved&lt;br /&gt;
* $fromobject is the object from where the customfields will be fetched (this can be a different module than $object, or can be null to be the same as $object)&lt;br /&gt;
* $outputlangs is the target language for translation of fields value (if empty, the default current global translation object will be used)&lt;br /&gt;
* $prefix allows you to save the custom fields in a sub-object. By default, when $prefix=null, custom fields are saved in the path $object-&amp;gt;customfields, but when you specify a prefix, custom fields will be saved inside $object-&amp;gt;customfields-&amp;gt;$prefix. This may be useful if you want to store multiple object&#039;s custom fields or records in the same $object.&lt;br /&gt;
* $pdfformat defines if you want a special formatting for the values: if null, there&#039;s no formatting, if false, there is a simple formatting (eg: translation of fields&#039; values), if true then the formatting will be adapted for PDF templating.&lt;br /&gt;
&lt;br /&gt;
For more informations on pratical usages of this function, please [[Module_CustomFields#Implementing_in_php_code_.28dolibarr_core_modules_or_your_own_module.29|read the user documentation on how to implement CustomFields in php code]] or [[Module_CustomFields_Cases#Linking_Dolibarr_objects_from_two_different_modules|read an example case of linking two objects of different modules together]].&lt;br /&gt;
&lt;br /&gt;
== customfields_fill_object_lines() ==&lt;br /&gt;
&lt;br /&gt;
Similar to customfields_fill_object() for normal custom fields, there is also a &#039;&#039;&#039;customfields_fill_object_lines()&#039;&#039;&#039; to fetch custom fields associated with lines of products/services. This function is also optimized and it is strongly advised to use it and to not try to fetch manually this kind of custom fields (ie: associated with lines of products/services rather than objects), because there are a lot of optimizations going on behind the scene and that are very hard to do manually.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields for lines of products/services (you can then access them by doing $object-&amp;gt;customfields-&amp;gt;lines-&amp;gt;$lineid-&amp;gt;cf_yourfield)&lt;br /&gt;
$customfields = customfields_fill_object_lines($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Requirements: $object can be a dummy object but must contain at least 2 properties: $object-&amp;gt;id and $object-&amp;gt;table_element_line.&lt;br /&gt;
&lt;br /&gt;
For more informations on pratical usages of this function, please [[Module_CustomFields#Implementing_in_php_code_.28dolibarr_core_modules_or_your_own_module.29|read the user documentation on how to implement CustomFields in php code]].&lt;br /&gt;
&lt;br /&gt;
= Architecture =&lt;br /&gt;
Here is a full list of the CustomFields packaged files with a short description (for a more in-depth view just crawl the source files, they are full of comments):&lt;br /&gt;
&lt;br /&gt;
== Core files ==&lt;br /&gt;
files that are necessary for the CustomFields to work, they contains the core functions&lt;br /&gt;
&lt;br /&gt;
* /customfields/admin/customfields.php --- Administrator&#039;s configuration panel : this is where you create and manage the custom fields definitions&lt;br /&gt;
* /customfields/class/actions_customfields.class.php --- Hooks class : used to hook into Dolibarr core modules without altering any core file (can be used to hook into your own modules too). Also used to preprocess the parameters before calling the printforms library.&lt;br /&gt;
* /customfields/class/customfields.class.php --- Core class : every database action is made here in this class. You can find some printing functions because they are very generic.&lt;br /&gt;
* /customfields/conf/conf_customfields.lib.php --- Configuration file : contains the main configurable variables to adapt CustomFields to your needs or to expand its support to other modules and more native sql types.&lt;br /&gt;
* /customfields/conf/conf_customfields_func.lib.php --- Auxiliary functions library to read and manage config variables&lt;br /&gt;
* /customfields/core/modules/modCustomFields.class.php --- Dolibarr&#039;s module definition file : this is a core file necessary for Dolibarr to recognize the module and to declare the hooks to Dolibarr (but it does not store anything else than meta-informations).&lt;br /&gt;
* /customfields/core/substitutions/functions_customfields.lib.php --- CustomFields substitution class for ODT generation : necessary to support customfields tags in ODT files&lt;br /&gt;
* /customfields/core/triggers/interface_50_modCustomFields_SaveFields.class.php --- Core triggers file : this is where the actions on records are managed. This is an interface between other modules and CustomFields management. You should not modify this file but you should add your triggers in $triggersarray inside conf_customfields.lib.php, but if you really have a special need and know what you do, you can edit this file to directly add your own triggers. Also, if CustomFields are shown in the forms but cannot be modified nor saved, then probably this trigger file is not detected by Dolibarr (maybe you can try to lower the number 50 to raise the priority?).&lt;br /&gt;
* /customfields/fields/customfields_fields_extend.lib.php.example --- Overloading functions library with users functions: this is where implementers can use their own php code to manage the custom fields they want (one function per action and per field).&lt;br /&gt;
* /customfields/fields/customfields_fields_ajax_custom.lib.php.example --- Custom AJAX functions library with users functions: this is where implementers can use their own php code to manage the custom fields cascading and AJAX calls, similarly to the Overloading functions library (one function per action and per field).&lt;br /&gt;
* /customfields/langs/code_CODE/customfields.lang --- Core language file : this is where you can translate the admin config panel (data types names, labels, descriptions, etc.)&lt;br /&gt;
* /customfields/langs/code_CODE/customfields-user.lang --- User defined language file : this is where you can store the labels and values of your custom fields (see the related chapter)&lt;br /&gt;
* /customfields/lib/customfields_aux.lib.php --- Simplifier library to easily use CustomFields class to populate an $object with custom fields datas (Facade design pattern). This is what you should use for most of your needs.&lt;br /&gt;
* /customfields/lib/customfields_printforms.lib.php --- Core printing library for records : contains only printing functions, there&#039;s not really any core functions inside but it allows to manage the printing of the custom fields records and their editing.&lt;br /&gt;
* /customfields/lib/customfields_ajax_wrapper.lib.php --- AJAX wrapper library to automatically manage cascading at objects creation. This isn&#039;t really necessary for CustomFields to function, this is more an addition to allow dynamical AJAX updating, but CustomFields core functionalities can work without this library.&lt;br /&gt;
* /customfields/sql/*.sql --- Unused (the tables are created directly via a function in the customfields.class.php) - but the sql files are still valid, they were used as squeletton in the first designs.&lt;br /&gt;
&lt;br /&gt;
== PDFTest module ==&lt;br /&gt;
optional module to append a page listing all custom fields values on newly generated PDF documents. To be used to test if custom fields are working correctly before making one&#039;s own template.&lt;br /&gt;
&lt;br /&gt;
* /customfieldspdftest/class/actions_customfieldspdftest.class.php --- Main functions library and hooks class, to hook onto pdf generation. Contains the PDFTest functions.&lt;br /&gt;
* /customfieldspdftest/core/modules/modCustomFieldsPDFTest.class.php --- Module descriptor for Dolibarr&lt;br /&gt;
&lt;br /&gt;
== Runtime outline ==&lt;br /&gt;
&lt;br /&gt;
Here is an outline of how CustomFields works: what functions will be called and why:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At admin page:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* No external function call (apart for some icons or needed admin functions like for tabs), everything that is done are sql queries via CustomFields class (to fetch and modify the sql columns in the customfields tables).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At printing:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Module&#039;s hook formObjectOptions calls Dolibarr&#039;s hookmanager&lt;br /&gt;
* Hookmanager calls CustomFields /class/actions_customfields.class.php, function formObjectOptions() of CustomFields&lt;br /&gt;
* Preprocess a few parameters (mainly the $action) and calls customfields_print_forms()&lt;br /&gt;
* customfields_print_forms() preprocess all the hook&#039;s parameters and assimilate them with the CustomFields&#039;s configuration parameters (there&#039;s no connection to the database at this stage, only parameters preprocessing), then calls /lib/customfields_printforms.lib.php, functions customfields_print_creation_form() or customfields_print_datasheet_form&lt;br /&gt;
* both customfields_print_creation_form() lookup the database using CustomFields class and prints empty custom fields; customfields_print_datasheet_form() lookup the database using CustomFields class, fetch the values of the custom fields and prints them;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At edition:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Same steps as &#039;&#039;&#039;At printing&#039;&#039;&#039;&lt;br /&gt;
* then customfields_print_datasheet_form() process the custom field being edited differently than the others: if editing, it prints the html input field (to enter a new value), or if saving it just saves the data into the database by calling the CUSTOMFIELDS_MODIFY trigger and then continue to print the other fields normally.&lt;br /&gt;
* CUSTOMFIELDS_MODIFY trigger calls $customfields-&amp;gt;update($newrecord);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At creation (saving):&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Module&#039;s creation trigger MYMODULE_CREATE calls Dolibarr&#039;s run_trigger()&lt;br /&gt;
* Dolibarr run_trigger() calls CustomFields trigger file /core/trigger/interface_50_modCustomFields_SaveFields.class.php, function run_trigger()&lt;br /&gt;
* CustomFields&#039;s run_trigger() assimilate the given parameters with the CustomFields&#039;s config parameters, and if the trigger is found in $triggersarray, then the appropriate action is taken (by default: _CREATE or another standard action if recognized), if not in $triggersarray, the function will still try to automatically detect the appropriate action by detecting MYMODULE_ALISTOFACTIONS with a regular expression.&lt;br /&gt;
* The appropriate action is then done (creation in this case) by calling $customfields-&amp;gt;create($record).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At edition or creation of product lines:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Similar to &#039;&#039;&#039;At edition&#039;&#039;&#039; but with hooks: formCreateProductOptions and formEditProductOptions.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At ODT document generation and substitution:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Module&#039;s ODT generator calls for Dolibarr&#039;s function complete_substitution_array(), which then calls for CustomFields substitution file /core/substitution/functions_customfields.lib.php, function customfields_completesubstitutionarray()&lt;br /&gt;
* CustomFields&#039;s function customfields_completesubstitutionarray() calls the simplifier library customfields_aux.lib.php, function customfields_fill_object() and make the substitution inside the $substitutionarray.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At ODT document generation and substitution for product lines:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Same as the previous point but this function is called for every product lines, and thus the data is cached. Also the simplifier function is here called customfields_fill_object_lines(), and the CustomFields&#039;s substitution function called customfields_completesubstitutionarray_lines().&lt;br /&gt;
&lt;br /&gt;
= How to add the support of a new module =&lt;br /&gt;
&lt;br /&gt;
There are two cases: either the module already implements hooks, and you just have to configure CustomFields to support it, or either the module does not implements hooks nor triggers.&lt;br /&gt;
&lt;br /&gt;
This chapter will guide you through the steps to make any module supported by CustomFields (be it a Dolibarr core module or your own module).&lt;br /&gt;
&lt;br /&gt;
== Preliminary test ==&lt;br /&gt;
&lt;br /&gt;
This preliminary test will allow you to know at a glance how much work needs to be done in order to make your module supported by CustomFields, and it will also give you all the config parameters if they are available.&lt;br /&gt;
&lt;br /&gt;
Important note: this test will only give data about hooks, which is the main part to make CustomFields work for your module. But for triggers, there is no way to test, so you will have to check the triggers manually (either by testing if the custom fields are saved at creation page, or by looking at the source code of the objects classes).&lt;br /&gt;
&lt;br /&gt;
Edit the php script of the module (check the URL when you access the module&#039;s datasheet or creation page - attention both are two different pages, so you have to do this twice), then put this at the very &#039;&#039;&#039;end&#039;&#039;&#039; of the php script:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// CustomFields debuglines to show config parameters for current module&lt;br /&gt;
print(&amp;quot;&amp;lt;pre&amp;gt;&amp;quot;); // html nice output&lt;br /&gt;
print(&amp;quot;table_element: {$object-&amp;gt;table_element} - table_element_line: {$object-&amp;gt;table_element_line} - contexts: &amp;quot;);&lt;br /&gt;
print_r($hookmanager-&amp;gt;contextarray);&lt;br /&gt;
print(&amp;quot; - conf hooks_modules: &amp;quot;);&lt;br /&gt;
print_r($conf-&amp;gt;hooks_modules);&lt;br /&gt;
print(&amp;quot; - hooks attached: &amp;quot;);&lt;br /&gt;
print_r($hookmanager-&amp;gt;hooks);&lt;br /&gt;
print(&amp;quot; - user rights: &amp;quot;);&lt;br /&gt;
print_r($user-&amp;gt;rights);&lt;br /&gt;
print(&amp;quot;&amp;lt;/pre&amp;gt;&amp;quot;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now reload the page, and scroll at the very bottom. You should see some informations about the module.&lt;br /&gt;
&lt;br /&gt;
Depending on the data available, you will know what you have to do:&lt;br /&gt;
&lt;br /&gt;
* If all the datas is available, then it&#039;s ok, jump to [[Module_CustomFields_Dev#Configuring_CustomFields_to_support_the_module]] and copy these datas to the parameters of conf_customfields.lib.php&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;table_element&#039;&#039;&#039;: $object is not defined, try to find how is called the main object variable and replace $object by this variable in the debug code given above (eg: in Agenda, the object variable is called $agenda and $agendacomm).&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;table_element_line&#039;&#039;&#039;: either there&#039;s no products lines for this module, either the main object variable is not called $object (see the previous point). One last case is that the object is not standard and does not store table_element_line (the name of the table that stores its products lines), in which case you have to manually add it in the class (eg: $this-&amp;gt;table_element_line = &#039;something&#039;).&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;contexts&#039;&#039;&#039;: the hookmanager is not defined, and probably there&#039;s no hook at all in this module, so you&#039;ll have to implement them by yourself. Jump to [[Module_CustomFields_Dev#Hooks]].&lt;br /&gt;
&lt;br /&gt;
* If there is a list of &#039;&#039;&#039;contexts&#039;&#039;&#039; but the module cannot be found in the list: the hookmanager is defined but the module has not defined a context, you still have to implement the hooks. Jump to [[Module_CustomFields_Dev#Hooks]].&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;conf hooks_modules&#039;&#039;&#039;: this variable is not necessary, empty or not it doesn&#039;t matter. But if you see customfields and/or customfieldspdftest inside the list, it is a confirmation that CustomFields indeed is hooking the module correctly.&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;hooks attached&#039;&#039;&#039;, or CustomFields cannot be found in the list: CustomFields has not been properly configured to hook to this module, or you forgot to disable then re enable CustomFields in order to update the CustomFields&#039;s hooks. Jump to [[Module_CustomFields_Dev#Configuring_CustomFields_to_support_the_module]].&lt;br /&gt;
&lt;br /&gt;
* If there is no &#039;&#039;&#039;$user-&amp;gt;rights&#039;&#039;&#039;: there is either no standard security implemented in this module, or either the variable is called with another name. If there is no security, you don&#039;t need to configure it in CustomFields, but if there is a security, you need to find how the variable is called (like $object can get another name sometimes).&lt;br /&gt;
&lt;br /&gt;
* If there is &#039;&#039;&#039;$user-&amp;gt;rights&#039;&#039;&#039; but the module can&#039;t be found in the list: either the name for the rights is different (this often happens, the rights array is not yet standardized), or either the rights are hardcoded into the module&#039;s code (rights should be announced to the $user-&amp;gt;rights array, but sometimes it happens that the dev forgot to do so, and thus the right is entirely hardcoded and you can&#039;t find it in the dynamic rights array). In any of these two cases, you will have to manually read the module&#039;s code to find the right. Also, maybe there is no security implemented, but since $user-&amp;gt;rights is declared this is less probable.&lt;br /&gt;
&lt;br /&gt;
Here is an example of output on a correctly hooked module (invoice):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
table_element: facture - table_element_line: facturedet - contexts: Array&lt;br /&gt;
(&lt;br /&gt;
    [0] =&amp;gt; searchform&lt;br /&gt;
    [1] =&amp;gt; leftblock&lt;br /&gt;
    [2] =&amp;gt; toprightmenu&lt;br /&gt;
    [3] =&amp;gt; invoicecard&lt;br /&gt;
)&lt;br /&gt;
 - conf hooks_modules: Array&lt;br /&gt;
(&lt;br /&gt;
    [customfields] =&amp;gt; invoicecard:propalcard:productcard:ordercard:thirdpartycard:contactcard:ordersuppliercard:invoicesuppliercard:membercard:actioncard:projectcard:projecttaskcard:contractcard:interventioncard:doncard:tripsandexpensescard:taxvatcard:expeditioncard:invoicecard:propalcard:ordercard:ordersuppliercard:invoicesuppliercard&lt;br /&gt;
    [customfieldspdftest] =&amp;gt; Array&lt;br /&gt;
        (&lt;br /&gt;
            [0] =&amp;gt; pdfgeneration&lt;br /&gt;
            [1] =&amp;gt; ordersuppliercard&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
)&lt;br /&gt;
 - hooks attached: Array&lt;br /&gt;
(&lt;br /&gt;
    [invoicecard] =&amp;gt; Array&lt;br /&gt;
        (&lt;br /&gt;
            [customfields] =&amp;gt; ActionsCustomFields Object&lt;br /&gt;
                (&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
)&lt;br /&gt;
 - user rights: stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [user] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [user] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [lire] =&amp;gt; 1&lt;br /&gt;
                    [creer] =&amp;gt; 1&lt;br /&gt;
                    [password] =&amp;gt; 1&lt;br /&gt;
                    [supprimer] =&amp;gt; 1&lt;br /&gt;
                    [export] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [self] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [creer] =&amp;gt; 1&lt;br /&gt;
                    [password] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [user_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [readperms] =&amp;gt; 1&lt;br /&gt;
                    [write] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [self_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [readperms] =&amp;gt; 1&lt;br /&gt;
                    [writeperms] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [group_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [read] =&amp;gt; 1&lt;br /&gt;
                    [readperms] =&amp;gt; 1&lt;br /&gt;
                    [write] =&amp;gt; 1&lt;br /&gt;
                    [delete] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    [facture] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [lire] =&amp;gt; 1&lt;br /&gt;
            [creer] =&amp;gt; 1&lt;br /&gt;
            [invoice_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [unvalidate] =&amp;gt; 1&lt;br /&gt;
                    [send] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [valider] =&amp;gt; 1&lt;br /&gt;
            [paiement] =&amp;gt; 1&lt;br /&gt;
            [supprimer] =&amp;gt; 1&lt;br /&gt;
            [facture] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [export] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    [propale] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [lire] =&amp;gt; 1&lt;br /&gt;
            [creer] =&amp;gt; 1&lt;br /&gt;
            [valider] =&amp;gt; 1&lt;br /&gt;
            [propal_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [send] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [cloturer] =&amp;gt; 1&lt;br /&gt;
            [supprimer] =&amp;gt; 1&lt;br /&gt;
            [export] =&amp;gt; 1&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    [produit] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [lire] =&amp;gt; 1&lt;br /&gt;
            [creer] =&amp;gt; 1&lt;br /&gt;
            [supprimer] =&amp;gt; 1&lt;br /&gt;
            [export] =&amp;gt; 1&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    etc... a lot of other rights...&lt;br /&gt;
&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When you are finished with implementing the support of CustomFields in the module, you should delete these debug lines.&lt;br /&gt;
&lt;br /&gt;
== Implementing the support of CustomFields in the module ==&lt;br /&gt;
&lt;br /&gt;
If there is a module you want to implement the support of CustomFields, but does not yet has the required components, here is a guide on how to implement these components.&lt;br /&gt;
&lt;br /&gt;
=== Hooks ===&lt;br /&gt;
&lt;br /&gt;
Implementing hooks will allow custom fields to appear on your module&#039;s page. They will also be editable and they will save the data. For creation, you need to use a trigger (see the subchapter below).&lt;br /&gt;
&lt;br /&gt;
First you should be familiar with the way that hooks are implemented in modules. If you don&#039;t, please first [[Hooks_system#Implementation|read the hooks documentation]].&lt;br /&gt;
&lt;br /&gt;
An example case of implementation of hooks into a Dolibarr core file can be found in the [[Module_CustomFields_Cases|CustomFields Example Cases wiki page]].&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Datasheet hook&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
To implement CustomFields in your module, you must initialize the hook manager, then use the following hook context:&lt;br /&gt;
* formObjectOptions&lt;br /&gt;
&lt;br /&gt;
This hook must be placed everywhere in your code where you want custom fields to appear (eg: on the creation page and datasheet view page).&lt;br /&gt;
&lt;br /&gt;
Note: Generally, you will have to place the hook just before the end of a table tag (&#039;&#039;&#039;&amp;lt;/table&amp;gt;&#039;&#039;&#039;), so if you don&#039;t know where to place it (eg: if you&#039;re implementing CustomFields on a Dolibarr core module), just search for this tag in the page shown in the url (eg: /htdocs/commande/fiche.php).&lt;br /&gt;
&lt;br /&gt;
Eg: First load the hookmanager at the beginning of your php script:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Initialize technical object to manage hooks of thirdparties. Note that conf-&amp;gt;hooks_modules contains array array&lt;br /&gt;
include_once(DOL_DOCUMENT_ROOT.&#039;/core/class/hookmanager.class.php&#039;);&lt;br /&gt;
$hookmanager=new HookManager($db);&lt;br /&gt;
$hookmanager-&amp;gt;initHooks(array(&#039;yourmodule&#039;));&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then place the hook as many times as required:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// ... a lot of php code ...&lt;br /&gt;
&lt;br /&gt;
print &#039;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&#039;; // end of the last row of a table&lt;br /&gt;
&lt;br /&gt;
// Other attributes&lt;br /&gt;
$parameters=array(&#039;colspan&#039; =&amp;gt; &#039; colspan=&amp;quot;3&amp;quot;&#039;); // specifying the colspan is optional but will help CustomFields know how to correctly print the table&lt;br /&gt;
$reshook=$hookmanager-&amp;gt;executeHooks(&#039;formObjectOptions&#039;,$parameters,$object,$action);    // Note that $action and $object may have been modified by hook&lt;br /&gt;
&lt;br /&gt;
print &#039;&amp;lt;/table&amp;gt;&#039;; // end of the table, this is what you&#039;re looking for, place the hook just above&lt;br /&gt;
&lt;br /&gt;
// ... a lot of php code ... redo the same if you find another &amp;lt;/table&amp;gt; and there&#039;s a page where there&#039;s no custom fields but you want to place custom fields on there&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note2: If after implementing the hook you receive an error or custom fields don&#039;t appear, you may have to change $object to another variable, depending on what is called the main object (eg: in Agenda it&#039;s called $action and $actioncomm).&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Lines hooks&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
For product lines, you should anyway use the product lines template files: htdocs/core/tpl/freeproductline_edit.tpl.php&lt;br /&gt;
&lt;br /&gt;
If you don&#039;t, you can still manually implement the required hooks, what is required is that you supply $line (which is the current line object being processed) in the $parameters array:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$parameters=array(&#039;line&#039;=&amp;gt;$line,&#039;fk_parent_line&#039;=&amp;gt;$line-&amp;gt;fk_parent_line);&lt;br /&gt;
&lt;br /&gt;
echo $hookmanager-&amp;gt;executeHooks(&#039;formEditProductOptions&#039;,$parameters,$this,$action);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hooks names:&lt;br /&gt;
* formEditProductOptions&lt;br /&gt;
* formCreateProductOptions ($line is not required here, because we create a new product line! you can leave $parameters=array(); empty).&lt;br /&gt;
Note: there&#039;s no hook for viewing, because showing the customfields will clutter the visual field, but if you really want you can also do it by adding a formViewProductOptions hook.&lt;br /&gt;
&lt;br /&gt;
=== Triggers ===&lt;br /&gt;
&lt;br /&gt;
First you need to know how to implement triggers in Dolibarr. For more informations about how to implement triggers in your module, please read the [[Triggers#Manage_and_create_a_new_trigger.27s_action|triggers wiki page]].&lt;br /&gt;
&lt;br /&gt;
An example case of implementation of hooks into a Dolibarr core file can be found in the [[Module_CustomFields_Cases|CustomFields Example Cases wiki page]].&lt;br /&gt;
&lt;br /&gt;
Implementing triggers on your module will allow CustomFields to save the fields at creation (datasheet edition is already managed by hooks).&lt;br /&gt;
&lt;br /&gt;
You need to implement 3 triggers:&lt;br /&gt;
- on creation of object - eg: YOURMODULE_CREATE&lt;br /&gt;
- on creation of lines - eg: YOURMODULELINE_INSERT (optional, only if your module has product lines)&lt;br /&gt;
- on update of lines - eg: YOURMODULELINE_UPDATE (optional, only if your module has product lines)&lt;br /&gt;
&lt;br /&gt;
Note: you need to supply the sql id of the newly created object before calling the trigger.&lt;br /&gt;
&lt;br /&gt;
Eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$this-&amp;gt;id = $this-&amp;gt;db-&amp;gt;last_insert_id(MAIN_DB_PREFIX.&amp;quot;yourmodule_table_element&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
// Appel des triggers&lt;br /&gt;
include_once(DOL_DOCUMENT_ROOT . &amp;quot;/core/class/interfaces.class.php&amp;quot;);&lt;br /&gt;
$interface=new Interfaces($this-&amp;gt;db);&lt;br /&gt;
$result=$interface-&amp;gt;run_triggers(&#039;YOURMODULE_CREATE&#039;,$this,$user,$langs,$conf);&lt;br /&gt;
if ($result &amp;lt; 0) {&lt;br /&gt;
    $error++; $this-&amp;gt;errors=$interface-&amp;gt;errors;&lt;br /&gt;
}&lt;br /&gt;
// Fin appel triggers&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note2: if it&#039;s a Dolibarr core module, you can easily find the right place to put the trigger by searching for &#039;create sql&#039; in the class subfolder (eg: URL is /htdocs/commande/fiche.php, the class is in /htdocs/commande/class/commande.class.php).&lt;br /&gt;
&lt;br /&gt;
Note3: CustomFields also supports a few other triggers like _CLONE, _UPDATE, _MODIFY, but they are not required and may not work depending on how your module works to manage these actions.&lt;br /&gt;
&lt;br /&gt;
== Configuring CustomFields to support the module ==&lt;br /&gt;
&lt;br /&gt;
=== Preliminary work ===&lt;br /&gt;
&lt;br /&gt;
You need to get the parameters of this module, and you will then put them in the CustomFields configuration file (conf_customfields.lib.php).&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Getting the parameters automatically&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
You can use the [[Module_CustomFields_Dev#Preliminary_test|preliminary test]] to easily get all those parameters.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Getting the parameters manually&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
To configure CustomFields to support a module, you need at least 5 parameters:&lt;br /&gt;
* the module&#039;s table_name (name of the sql table where the module stores its datas)&lt;br /&gt;
* the module&#039;s context (name given by the module to the hookmanager)&lt;br /&gt;
* the module&#039;s idvar (name of the variable that holds the sql row id of the record)&lt;br /&gt;
* the module&#039;s trigger action(s) (triggers on creation and update, see the related chapter above)&lt;br /&gt;
* the module&#039;s rights (for security purposes)&lt;br /&gt;
&lt;br /&gt;
To get these parameters manually, you can take a look inside the code of the module&#039;s files you want to implement:&lt;br /&gt;
* module&#039;s table name: either find it by yourself in the database (looks like llx_themodulename) or by printing it in actions_customfields.class.php by adding &#039;&#039;&#039;print_r($parameters)&#039;&#039;&#039; and search for $parameters-&amp;gt;table_element&lt;br /&gt;
* module&#039;s context: search for &amp;quot;callHooks(&amp;quot; without the quotes or take a look at the wiki: http://wiki.dolibarr.org/index.php/Hooks_system&lt;br /&gt;
or you can get it by printing it in the /htdocs/customfields/class/actions_customfields.class.php by adding print_r($parameters) and search for $parameters-&amp;gt;context&lt;br /&gt;
* module&#039;s trigger actions: search for &amp;quot;run_triggers(&amp;quot; or take a look at the wiki: http://wiki.dolibarr.org/index.php/Triggers#List_of_known_triggers_actions&lt;br /&gt;
* module&#039;s idvar: look at the url when you access one of the module&#039;s record, and you should see something like fiche.php?xxxid=12 - you need the xxxid (eg: socid, facid, etc.).&lt;br /&gt;
* module&#039;s security: in the page shown in URL when accessing the module&#039;s datasheet, edit this file and add &#039;&#039;&#039;print($user-&amp;gt;rights)&#039;&#039;&#039;, then in the list try to find the correct right (generally something like $user-&amp;gt;modulename-&amp;gt;creer). You can also try to search for $user-&amp;gt;rights in the sourcecode of this module.&lt;br /&gt;
&lt;br /&gt;
With these values, edit the config file (/htdocs/customfields/conf/conf_customfields.lib.php), particularly the $modulesarray and $triggersarray variables:&lt;br /&gt;
&lt;br /&gt;
=== Configure hooks and main parameters ===&lt;br /&gt;
&lt;br /&gt;
Add every parameters except triggers in the $modulesarray:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$modulesarray = array(&lt;br /&gt;
                      array(&#039;context&#039;=&amp;gt;&#039;invoicecard&#039;, &#039;table_element&#039;=&amp;gt;&#039;facture&#039;, &#039;idvar&#039;=&amp;gt;&#039;facid&#039;, &#039;rights&#039;=&amp;gt;array(&#039;$user-&amp;gt;rights-&amp;gt;facture-&amp;gt;creer&#039;)), // Client Invoice&lt;br /&gt;
                      array(&#039;context&#039;=&amp;gt;&#039;membercard&#039;, &#039;table_element&#039;=&amp;gt;&#039;adherent&#039;, &#039;idvar&#039;=&amp;gt;&#039;rowid&#039;, &#039;rights&#039;=&amp;gt;array(&#039;$user-&amp;gt;rights-&amp;gt;adherent-&amp;gt;creer&#039;), &#039;tabs&#039;=&amp;gt;array(&#039;objecttype&#039;=&amp;gt;&#039;member_admin&#039;, &#039;function&#039;=&amp;gt;&#039;member_admin_prepare_head&#039;, &#039;lib&#039;=&amp;gt;DOL_DOCUMENT_ROOT.&#039;/core/lib/member.lib.php&#039;)), // Members / Adherents&lt;br /&gt;
                     ); // Edit me to add the support of another module - NOTE: Lowercase only!&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are also a lot of others parameters you can give to CustomFields so that the module gets better managed or to add a few other functionnalities, here is a non-exhaustive list:&lt;br /&gt;
* context: name of the module&#039;s context.&lt;br /&gt;
* table_element: name of the sql table that holds the object&#039;s datas.&lt;br /&gt;
* idvar: name of the variable that holds the id of the object in the sql database (by default &#039;id&#039; or &#039;rowid&#039;).&lt;br /&gt;
* rights: prevent the custom fields to be editable unless all the rights set here are enabled (preventing custom fields to be viewed is useless because they won&#039;t show anyway if the page can&#039;t be loaded due to insufficient rights, so this is directly handled by the module).&lt;br /&gt;
* tabs=&amp;gt;array(objecttype, function, lib, tabname, tabtitle): used to embed CustomFields&#039;s admin panel into another module&#039;s admin page.&lt;br /&gt;
&lt;br /&gt;
=== Configure creation triggers ===&lt;br /&gt;
&lt;br /&gt;
Add the triggers in $triggersarray:&lt;br /&gt;
$triggersarray = array(&amp;quot;order_create&amp;quot;=&amp;gt;&amp;quot;commande&amp;quot;,&lt;br /&gt;
                       &amp;quot;yourmodule_triggeraction1&amp;quot;=&amp;gt;&amp;quot;yourmodule_tablename&amp;quot;,&lt;br /&gt;
                       &amp;quot;yourmodule_triggeraction2&amp;quot;=&amp;gt;&amp;quot;yourmodule_tablename&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
Note: generally you will be looking to implement the _CREATE.&lt;br /&gt;
&lt;br /&gt;
Note2: if a triggeraction is not recognized by CustomFields (eg: mymodule_addpayment) then by default CustomFields will assimilate it to a _create action (it will create and save the values for the custom fields, just for a module&#039;s creation page).&lt;br /&gt;
&lt;br /&gt;
Note3: CustomFields also supports a few other triggers like _CLONE, _UPDATE, _MODIFY, but they are not required and may not work depending on how your module works to manage these actions.&lt;br /&gt;
&lt;br /&gt;
=== Configuration troubleshooting ===&lt;br /&gt;
&lt;br /&gt;
* If you cannot see the custom fields appear on your module&#039;s page, this means the hooks are not implemented or configured in CustomFields, please read the related chapters.&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet but there&#039;s no edit button icon: the module implements security but you did not configure it properly in CustomFields, please find the right security in $user-&amp;gt;rights and set it in conf_customfields.lib.php.&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the creation page but are not saved: you forgot to set the trigger in $triggersarray, or the creation trigger does not exist for this module and you have to implement it. Jump to [[Module_CustomFields_Dev#Triggers]].&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet but not on the creation page (or vice versa): A hook is missing on the page where the custom fields do not appear. Jump to [[Module_CustomFields_Dev#Hooks]].&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet with the edit button, but when the edit button is clicked, everything disappear: either the $idvar is not properly set (look at the URL to know if you need to set idvar), or the hook is not properly made ($object is not defined and it&#039;s called by another name, but the hook use $object instead of the correct variable).&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet with the edit button, can be edited but cannot be saved (the data is not saved): same as the previous point: either $idvar or $object bug.&lt;br /&gt;
&lt;br /&gt;
Also you might find useful to &#039;&#039;&#039;[[Module_CustomFields#General_advices_for_troubleshooting|enable php errors]]&#039;&#039;&#039; to better track errors if that&#039;s not already done.&lt;br /&gt;
&lt;br /&gt;
= Example cases =&lt;br /&gt;
&lt;br /&gt;
You can find a compilation of concrete, practical examples on the [[Module_CustomFields_Cases]] wiki page.&lt;br /&gt;
&lt;br /&gt;
= TO DO =&lt;br /&gt;
&lt;br /&gt;
You will find below the public list of features that should be implemented in the future.&lt;br /&gt;
&lt;br /&gt;
If you have an idea about a new feature that isn&#039;t listed below, feel free to add it to the list!&lt;br /&gt;
&lt;br /&gt;
== To Do ==&lt;br /&gt;
Nothing here!&lt;br /&gt;
&lt;br /&gt;
== To Document ==&lt;br /&gt;
* Automatic recopy on cloning for custom fields and products&#039; lines custom fields.&lt;br /&gt;
* new API function: customfields_clone_or_recopy($object, $fromobject, $action2 = null)&lt;br /&gt;
&lt;br /&gt;
== Should do ==&lt;br /&gt;
* Add the possibility to mark a custom field to be shown in tables listings (eg: as a column in products listings) with &amp;quot;order by this column&amp;quot; buttons and stuffs. Will be difficult if the hooks are not yet implemented in listings.&lt;br /&gt;
* Checkbox (multiple checkboxes, one for each possible value) and multiselect (big select and can use SHIFT to multiselect) (store in XML file) (but impossible with constrained fields, can&#039;t store multiple foreign keys ids in one single sql field, but would work for other types like enum)&lt;br /&gt;
use fieldset, input checkbox and label for, eg:&lt;br /&gt;
    &amp;lt;fieldset&amp;gt;&amp;lt;legend&amp;gt;&amp;lt;label for=&amp;quot;cf_productslist&amp;quot;&amp;gt; $langs-&amp;gt;trans(&#039;cf_productslist&#039;)&amp;lt;/label&amp;gt;&amp;lt;/legend&amp;gt;&lt;br /&gt;
    &amp;lt;input type=&amp;quot;checkbox&amp;quot; name=&amp;quot;cf_productslist_choice1&amp;quot; value=&amp;quot;1&amp;quot; id=&amp;quot;cf_productslist_choice1&amp;quot; /&amp;gt;&amp;lt;label for=&amp;quot;cf_productslist_choice1&amp;quot;&amp;gt; $langs-&amp;gt;trans(&#039;cf_productslist_choice1&#039;)&amp;lt;/label&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
    &amp;lt;input type=&amp;quot;checkbox&amp;quot; name=&amp;quot;cf_productslist_choice2&amp;quot; value=&amp;quot;1&amp;quot; id=&amp;quot;cf_productslist_choice2&amp;quot; /&amp;gt;&amp;lt;label for=&amp;quot;cf_productslist_choice2&amp;quot;&amp;gt; $langs-&amp;gt;trans(&#039;cf_productslist_choice2&#039;)&amp;lt;/label&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
    etc...&lt;br /&gt;
    &amp;lt;/fieldset&amp;gt;&lt;br /&gt;
* Add a javascript options values generator for the enum type (a hidden input that would be shown only when DropdownBox type is selected and that would permit to add options by clicking a plus button).&lt;br /&gt;
* Replace triggers in customfields class by hookmanager?&lt;br /&gt;
* AJAX previsualisation of the HTML custom field that will be created.&lt;br /&gt;
* In ODTs, create special tags to directly access day, month or year value of a date type field independently of the rest.&lt;br /&gt;
* New overloading function&#039;s action: &amp;quot;get&amp;quot;, similar to view except that it works not only on Dolibarr&#039;s view but whenever someone tries to access this field&#039;s value.&lt;br /&gt;
* Implementation of custom fields in CSV exports (may need a few new hooks in Dolibarr?).&lt;br /&gt;
* Add Upload and Image field types (this would allow for an easier integration inside ODT and PDF templates than the current workflow of Dolibarr).&lt;br /&gt;
&lt;br /&gt;
== Known bugs ==&lt;br /&gt;
* in product and service modules, if you edit a field, the proposals and other fields below won&#039;t be shown, you need to refresh the page. This problem resides in Dolibarr I think (since we are simply using a hook).&lt;br /&gt;
* ficheinter (Intervention module) custom fields will stay in create mode just after create page is submitted (this is because of the $action that is not standard - $action=edit or something).&lt;br /&gt;
&lt;br /&gt;
== Never/Maybe one day ==&lt;br /&gt;
* Add support for repeatable (predefined) invoices (the way it is currently managed makes it very difficult to manage this without making a big exception, adding specific functions in customfields modules that would not at all will be reusable anywhere else, when customfields has been designed to be as generic as possible to support any module and any version of dolibarr, because it&#039;s managed by a totally different table while it&#039;s still managed by the same module, CustomFields work with the paradigm: one module, one table).&lt;br /&gt;
* Add an AJAX select box for constrained values : when a constrained type is selected and a table is selected, a hidden select box would show up with the list of the fields of this table to choose the values that will be printed as the values for this customfield (eg: for table llx_users you could select the &amp;quot;nom&amp;quot; field and then it would automatically prepend &amp;quot;nom_&amp;quot; to the field&#039;s name).&lt;br /&gt;
* Fine-grained rights management: rights per group, per user, and per field.&lt;br /&gt;
* Replace overloading functions (extend class) by hookmanager if possible (but how to simulate the *full switch? Plus this will force users to make their own modules to make those functions, and to disable/renable their modules everytime they will add a new function...).&lt;br /&gt;
* Refactor the trigger array: merge it with the modulesarray (along with a new way to specify which customfields trigger action a trigger should correspond, eg: &#039;linebill_insert&#039;=&amp;gt;&#039;customfields_create&#039;). But the triggers aren&#039;t necessarily associated with a specific module...&lt;/div&gt;</summary>
		<author><name>Lrq3000</name></author>
	</entry>
	<entry>
		<id>https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Dev&amp;diff=33136</id>
		<title>Module CustomFields Dev</title>
		<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Dev&amp;diff=33136"/>
		<updated>2015-02-03T22:39:36Z</updated>

		<summary type="html">&lt;p&gt;Lrq3000: /* Simple printing in documents */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:CustomFields]]&lt;br /&gt;
{{TemplateDocDevEn}}&lt;br /&gt;
{{TemplateModEN}}&lt;br /&gt;
{{BasculeDevUserEn|&lt;br /&gt;
name=CustomFields|&lt;br /&gt;
num=8500|&lt;br /&gt;
devdoc=This page|&lt;br /&gt;
userdoc=[[Module_CustomFields]]|}}&lt;br /&gt;
[[Category:FAQ EN]]&lt;br /&gt;
&lt;br /&gt;
These notes are aimed to developpers who want to extend the functionnalities of the module or want to use it for extreme cases.&lt;br /&gt;
This is not meant to guide implementers on how to implement and configure the module (see the related chapter for that).&lt;br /&gt;
&lt;br /&gt;
If you are an implementer and just want to use CustomFields with its normal functions, you can read the [[Module_CustomFields|user documentation of CustomFields]].&lt;br /&gt;
&lt;br /&gt;
You can also jump directly to [[Module_CustomFields_Cases]] wiki page for a collection of practical examples.&lt;br /&gt;
&lt;br /&gt;
= Principles =&lt;br /&gt;
&lt;br /&gt;
CustomFields is simply a wrapper for SQL databases.&lt;br /&gt;
&lt;br /&gt;
The module is based on SQL to manage the fields and most of the stuff. It uses and understands standard SQL.&lt;br /&gt;
&lt;br /&gt;
The main principles of CustomFields are:&lt;br /&gt;
&lt;br /&gt;
* one SQL column = one Dolibarr field.&lt;br /&gt;
* one SQL type = one method to show and edit the field in Dolibarr.&lt;br /&gt;
* almost everything should be configurable from the configuration file, or admin interface.&lt;br /&gt;
* be independent from Dolibarr&#039;s data structures.&lt;br /&gt;
&lt;br /&gt;
CustomFields sits besides Dolibarr, and does not mix with it, even if in the Dolibarr&#039;s interface it looks like CustomFields is embedded inside Dolibarr, in reality, CustomFields is totally independent.&lt;br /&gt;
&lt;br /&gt;
= Goals =&lt;br /&gt;
&lt;br /&gt;
This module was made to provide an easy way to manage custom sql fields, not only for users and implementers, but also for developpers to use in their own module:&lt;br /&gt;
* by easily adding the support of custom fields for your own module by using this module;&lt;br /&gt;
* by reusing custom fields from another module in your own module;&lt;br /&gt;
* by managing sql fields in your own module by using the CustomFields class (which will take care of everything from printing, editing to PDF/ODT templating).&lt;br /&gt;
&lt;br /&gt;
So this module is not only an auxiliary of other modules, but it can also be the core part of your own module by using it as your SQL query class for fields.&lt;br /&gt;
&lt;br /&gt;
= SQL structure =&lt;br /&gt;
&lt;br /&gt;
A CustomFields table needs to respect three parameters that never change:&lt;br /&gt;
&lt;br /&gt;
* table&#039;s name (format: llx_*dolibarrmodule*_customfields where *dolibarrmodule* = table_element in conf_customfields.lib.php)&lt;br /&gt;
* first column: rowid, which is the primary key for each field&lt;br /&gt;
* second column: fk_*dolibarrmodule* which is the foreign key that links every customfields record to the Dolibarr&#039;s module that is associated (invoice, order, etc...).&lt;br /&gt;
&lt;br /&gt;
Apart from the table&#039;s name and the first two special columns, every other columns are just standard sql columns that you can freely create, edit or delete as you want, from the module&#039;s administration page or from a third-party SQL management tool (such as phpMyAdmin).&lt;br /&gt;
&lt;br /&gt;
You can also create more complex sql fields: constraints, foreign keys, transformations, other sql types, etc...).&lt;br /&gt;
&lt;br /&gt;
Then the module will try to manage the sql fields you manually created the best it can:&lt;br /&gt;
* by printing appropriate input fields in the Dolibarr gui interface.&lt;br /&gt;
* by providing the developper an easy access to these fields in php (eg: $customfields-&amp;gt;cf_myfield or $object-&amp;gt;customfields-&amp;gt;cf_myfield) for any use, including PDF templates and ODT templates, or in any php script (so that you can use these fields in your own module).&lt;br /&gt;
&lt;br /&gt;
= SQL Indexes =&lt;br /&gt;
&lt;br /&gt;
The module automatically adds an index on the primary key and foreign key (first and second columns).&lt;br /&gt;
&lt;br /&gt;
To further optimize the performances of your database, and particularly the customfields tables, you may want to manually add indexes based on usage.&lt;br /&gt;
&lt;br /&gt;
Also, you may add indexes on remote tables used by constrained fields, which could optimize even more the performances since constrained fields have the worst performance drawback for CustomFields.&lt;br /&gt;
&lt;br /&gt;
= SQL Compatibility mode =&lt;br /&gt;
&lt;br /&gt;
The module relies on referential integrity checks and foreign keys of your DBMS to keep the database clean and to provide the constrained fields feature. Referential integrity checks is part of the SQL standards and should be used whenever possible, which always was a main goal for CustomFields (to be respectful of SQL standards).&lt;br /&gt;
&lt;br /&gt;
Unluckily, a few DBMS systems still don&#039;t support this critical functionality of the SQL standard, for example MySQL enables MyISAM engine by default, which doesn&#039;t support this feature.&lt;br /&gt;
&lt;br /&gt;
In these cases, you can either do two things:&lt;br /&gt;
* Switch to an engine which support referential integrity. For example with MySQL, you can enable InnoDB, which support referential integrity checks and foreign keys.&lt;br /&gt;
* Use CustomFields compatibility mode (more infos below).&lt;br /&gt;
&lt;br /&gt;
In the case your database does not support referential integrity checks, CustomFields will automatically switch to the SQL compatibility mode, which will allow you to use CustomFields even on databases where foreign keys are not possible.&lt;br /&gt;
&lt;br /&gt;
In compatibility mode, you will get nearly all CustomFields advanced features such as Constrained fields, but the customfields&#039; tables in the database won&#039;t be cleaned automatically (no delete on cascade, meaning that when you delete an object, the linked custom fields will still be kept in your database), and constrained fields won&#039;t be updated automatically on update.&lt;br /&gt;
&lt;br /&gt;
Nevertheless, these missing cleanup functionalities won&#039;t be seen by your users because they will never be accessible: on deletion of an object, the custom fields won&#039;t ever be accessible again from Dolibarr&#039;s interface, and object&#039;s ids are never changed since Dolibarr also relies on this. Thus in compatibility mode, CustomFields will act just like normally, but your database will be a bit less clean.&lt;br /&gt;
&lt;br /&gt;
= JSON extension for extra options =&lt;br /&gt;
&lt;br /&gt;
== Introduction to JSON in CustomFields ==&lt;br /&gt;
The relational model is very great to store and fetch data with a declarative language, and this is very powerful, but this model also imposes a few limitations, for example the relations must be well formatted and predefined.&lt;br /&gt;
&lt;br /&gt;
That&#039;s why we finally implemented a &#039;&#039;&#039;JSON extension&#039;&#039;&#039; to manage &#039;&#039;&#039;extra options&#039;&#039;&#039; and associate them with custom fields. This allows to add many properties (or columns if you are more used to the relational model) on-the-fly without having to predefine them prior to storing and using them.&lt;br /&gt;
&lt;br /&gt;
This allows to add an undefined number of columns/properties/extra options that will help manage special behaviours for custom fields. For example, it is currently used to allow to change the order of appearance of custom fields, and to store and manage remote table and columns datas for constrained fields on DBMS that do not support Foreign Keys checks.&lt;br /&gt;
&lt;br /&gt;
We chose JSON over XML because the library are simpler and the data footprint smaller, and because CustomFields does not need advanced features of XML like XQuery, data typing and meta structures (at least not for now).&lt;br /&gt;
&lt;br /&gt;
The other advantage is that php objects can directly be encoded into JSON without any preprocessing, and can then as straightly be decoded from JSON back into a php object.&lt;br /&gt;
&lt;br /&gt;
A simple mean to encode and decode back an object into and from a storage place, as well as allow users to easily add/remove extra options were the main requirement, and JSON (+ JSON libraries for PHP) meet this criteria.&lt;br /&gt;
&lt;br /&gt;
However, note that the extra options are now encoded as an associative array, so as to avoid any ambiguities (there&#039;s no associative array in JSON, they are converted to objects).&lt;br /&gt;
&lt;br /&gt;
== Implementation of JSON ==&lt;br /&gt;
The implementation is very simple: a table called &#039;&#039;&#039;llx_customfields_extraoptions&#039;&#039;&#039; is created when you enable the CustomFields module, and this table only contains three columns:&lt;br /&gt;
* table_name (varchar)&lt;br /&gt;
* column_name (varchar)&lt;br /&gt;
* extraoptions (blob)&lt;br /&gt;
&lt;br /&gt;
(table_name, column_name) both forms the primary key of the table, and links to columns (custom fields) of other customfields tables.&lt;br /&gt;
extraoptions is a blob, which allows to store the data as binary (as-is) without encoding (which solves a big problem of JSON which is encoding special and foreign characters, here there is no problem at all).&lt;br /&gt;
&lt;br /&gt;
When fetching custom fields, the module simply JOIN the extraoptions table, which adds an &#039;&#039;&#039;extraoptions&#039;&#039;&#039; column for each custom field in the SQL result. Then it is quite easy to just json_decode() the data returned inside the extraoptions column.&lt;br /&gt;
&lt;br /&gt;
This methodology allows to have all the advantages of a relational database, with the flexibility of a free-schema model:&lt;br /&gt;
* everything is stored inside the database (only one storage place)&lt;br /&gt;
* fixed columns allows for relational joining and optimization&lt;br /&gt;
* thus queries (fetching) are optimised&lt;br /&gt;
* at the same time, the extraoptions are automatically retrieved and associated with every custom field, and whenever needed they can be used&lt;br /&gt;
* and thus we also get a flexible model to &#039;&#039;&#039;add extra (virtual) columns whenever needed&#039;&#039;&#039; without having to alter the relational database schema&lt;br /&gt;
&lt;br /&gt;
Thus no critical information is and should be stored inside the extraoptions column because they would not be query-able, only extra options that are just used to tweak the management of those fields inside the PHP application should be stored. But this is a wonderful way to add non critical informations that still allows to develop more complex features and management for special custom fields.&lt;br /&gt;
&lt;br /&gt;
== CustomFields JSON API ==&lt;br /&gt;
&lt;br /&gt;
=== Storing extra options ===&lt;br /&gt;
&lt;br /&gt;
You can use the &#039;&#039;&#039;setExtra()&#039;&#039;&#039; method to set extra options of a custom field.&lt;br /&gt;
&lt;br /&gt;
Eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$extra = array(); // $extra should always be an associative array, it&#039;s no longer an object&lt;br /&gt;
$extra[&#039;myproperty&#039;] = &#039;Anything I want here&#039;; // set a property/extra option like this&lt;br /&gt;
$extra[&#039;category&#039;][&#039;subcategory&#039;][&#039;prop&#039;] = true; // we can set recursive properties, there&#039;s not any limit to how deep the hierarchy can be&lt;br /&gt;
&lt;br /&gt;
require_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomField($db, &#039;currentmodule&#039;);&lt;br /&gt;
$fieldid = &#039;mycustomfields&#039;; // $fieldid can either be a column_name or an ordinal_position from information_schema.columns&lt;br /&gt;
$result = $customfields-&amp;gt;setExtra($fieldid, $extra); // store the extra options inside the database&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can also use addCustomField() or updateCustomField() with an $extra array, and these functions will also call the setExtra() method.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: the extra options you set are &#039;&#039;&#039;appended&#039;&#039;&#039; to the ones already stored in the database. However, you can also overwrite previous options, just use the same name of property but with a different value (the replacement is recursive, you can do it at any level in the hierarchy).&lt;br /&gt;
&lt;br /&gt;
=== Fetching extra options ===&lt;br /&gt;
&lt;br /&gt;
Extra options are automatically fetched along with a custom field structure, decoded, and stored inside the extra sub-property of any field object, or CustomFields object.&lt;br /&gt;
&lt;br /&gt;
When fetched, extra options are stored as a sub-object for the current field object (which itself is stored as a subobject of the CustomFields object if caching is enabled).&lt;br /&gt;
&lt;br /&gt;
Eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
require_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomField($db, &#039;currentmodule&#039;);&lt;br /&gt;
$fields = fetchAllFieldsStruct(); // fetching fields structure will automatically fetch extra options at the same time. This works with fetchAllFieldsStruct() and fetchFieldStruct()&lt;br /&gt;
&lt;br /&gt;
// Now that you fetched the fields structure, you can just access the extra options sub-properties:&lt;br /&gt;
print_r($fields-&amp;gt;mycustomfield-&amp;gt;extra); // will print all extra options for the field mycustomfield&lt;br /&gt;
print_r($customfields-&amp;gt;fields-&amp;gt;mycustomfield-&amp;gt;extra); // you can also access the same field(s) directly from the CustomFields object if caching is enabled (default behaviour)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Modifying extra options ===&lt;br /&gt;
&lt;br /&gt;
You can just combine the two previous points to edit extra options of a custom field: just load a custom field, edit on-the-fly the extra property with PHP code, and then store it back directly into the database:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
require_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomField($db, &#039;currentmodule&#039;);&lt;br /&gt;
$fields = fetchAllFieldsStruct();&lt;br /&gt;
&lt;br /&gt;
$fields-&amp;gt;mycustomfield-&amp;gt;extra[&#039;newproperty&#039;] = &#039;This is a new property&#039;; // add a new property over the old extra options sub-object&lt;br /&gt;
$fields-&amp;gt;mycustomfield-&amp;gt;extra[&#039;mycategory&#039;] = &#039;Overwrite the old category&#039;; // this will replace the previous value of &#039;mycategory&#039; extra option&lt;br /&gt;
&lt;br /&gt;
$customfields-&amp;gt;setExtra(&#039;mycustomfield&#039;, $fields-&amp;gt;mycustomfield-&amp;gt;extra); // store the modified extra options back into the database for future usage&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= CustomFields API =&lt;br /&gt;
&lt;br /&gt;
== Introduction to CustomFields API ==&lt;br /&gt;
&lt;br /&gt;
This chapter will cover the basics on how to manually and concretely use the CustomFields class to create/update/fetch/manage custom fields.&lt;br /&gt;
&lt;br /&gt;
This will give you a better understanding of how CustomFields works, as well as giving you insights on how to use it for your own applications.&lt;br /&gt;
&lt;br /&gt;
Important note: contrary to the Facade API, the core CustomFields class use columns names without any prefix, so don&#039;t try to use the prefix, it won&#039;t work! For example: if your field is named &#039;mycustomfield&#039; and prefix is &#039;cf_&#039;, when you use the Facade API you should use &#039;$object-&amp;gt;$path-&amp;gt;cf_mycustomfield&#039; but when you use the core CustomFields class directly, $fieldname = &#039;mycustomfield&#039;.&lt;br /&gt;
&lt;br /&gt;
== Prerequisite to use the CustomFields class ==&lt;br /&gt;
&lt;br /&gt;
Before using the CustomFields class, you first must instanciate it. This is a necessary preliminary step before using any of the methods that will be covered inside this chapter, and thus will not be repeated, only shown once here.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Include the CustomFields class (which contains the main API)&lt;br /&gt;
require_once(dirname(__FILE__).&#039;/../class/customfields.class.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Instanciate a CustomFields object&lt;br /&gt;
global $db; // the database handler created by Dolibarr&lt;br /&gt;
$table_element = &#039;currentmodule&#039;; // where $table_element is the database&#039;s table name for the module, without the Dolibarr prefix (eg: for &#039;llx_facture&#039;, table_element is &#039;facture&#039;). You can also find it inside the $object properties (just print_r() it).&lt;br /&gt;
$customfields = new CustomFields($db, $table_element); // instanciate the CustomFields class inside $customfields object&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
CustomFields keeps track of errors that can happen when executing methods and SQL queries inside the &#039;&#039;&#039;error&#039;&#039;&#039; and &#039;&#039;&#039;errors&#039;&#039;&#039; properties, which you can print with the printErrors() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Do something with CustomFields before&lt;br /&gt;
&lt;br /&gt;
if (count($customfields-&amp;gt;errors) &amp;gt; 0) { // If there is at least one error&lt;br /&gt;
    $customfields-&amp;gt;printErrors(); // print errors&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// You can also print your own error message, in two manners:&lt;br /&gt;
&lt;br /&gt;
// 1- submit your own message&lt;br /&gt;
$customfields-&amp;gt;printErrors(&#039;Your own error message here&#039;); // this will print ONLY your error message, using the Dolibarr&#039;s AJAX error dialog (standard way of printing errors)&lt;br /&gt;
&lt;br /&gt;
// 2- append your message in the customfields errors&lt;br /&gt;
$customfields-&amp;gt;errors[] = &#039;Your own error message to be appended with other CustomFields messages&#039;;&lt;br /&gt;
$customfields-&amp;gt;printErrors(); // Here your error message will be appended, thus all error messages will be printed. This allows you for example to add more intelligible informations on a specific error.&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== General purpose SQL methods ==&lt;br /&gt;
&lt;br /&gt;
CustomFields provide an interface to easily execute various common SQL requests, as well as your own custom SQL requests.&lt;br /&gt;
&lt;br /&gt;
* fetchAllTables() fetch a list of all the tables in the database&lt;br /&gt;
* fetchPrimaryField($table) fetch the column_name (and only the column_name!) of the primary field of a table. You can then use fetchFieldStruct()  with the column_name to get any information you want.&lt;br /&gt;
* fetchAny($columns, $table, $where=&#039;&#039;, $orderby=&#039;&#039;, $limitby=&#039;&#039;) allows you to issue any simple SQL query command, for example fetchAny(&#039;*&#039;, &#039;sometable&#039;) is equivalent to SELECT * FROM sometable; (but then everything is managed automatically and result is returned to you)&lt;br /&gt;
* executeSQL($sql, $eventname) to execute any SQL query you want, this will return you a resource (and NOT a processed nuplet like fetchAny!). $eventname can be anything, it&#039;s just for Dolibarr logging facility.&lt;br /&gt;
* executeMultiSQL($sql, $eventname) to execute multiple SQL queries&lt;br /&gt;
* fetchFieldStruct($id=null, $nohide=false, $table=null, $cachepath=null) allows you to fetch the field&#039;s SQL structure of not only custom fields, but of any database&#039;s column using the $table variable. Eg: fetchFieldStruct(&#039;mycolumn&#039;, true, &#039;mytable&#039;);&lt;br /&gt;
* probeTable($table) helps you to check if a table exists in the database&lt;br /&gt;
* fetch_object($resource) will return a PHP object from a $resource, similarly to mysql_fetch_object() but with two advantages: it works on several DBMS (not only mysql), and it always return lowercase properties names (else depending on the configuration of the DBMS, the request can return uppercase or mixed case properties names).&lt;br /&gt;
* escape() and reverse_escape() are similare to mysql_real_escape but is reversible and DBMS-independent (so you can use it with other DBMSes).&lt;br /&gt;
&lt;br /&gt;
== Managing CustomFields structure ==&lt;br /&gt;
&lt;br /&gt;
The first step into CustomFields is to create and manage the structure of a custom field.&lt;br /&gt;
&lt;br /&gt;
=== Creating a custom field definition ===&lt;br /&gt;
&lt;br /&gt;
To create a custom field, you can use the addCustomField() method.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;addCustomField($fieldname, $type, $size, $nulloption, $defaultvalue = null, $constraint = null, $customtype = null, $customdef = null, $customsql = null, $fieldid = null, $extra = null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will take care of forging the (complicated) SQL query with the forgeSQLCustomField() method, and will return you an error if there was a problem.&lt;br /&gt;
&lt;br /&gt;
=== Updating a custom field definition ===&lt;br /&gt;
&lt;br /&gt;
To update a custom field, you can use the updateCustomField() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;&lt;br /&gt;
updateCustomField($fieldid, $fieldname, $type, $size, $nulloption, $defaultvalue, $constraint = null, $customtype = null, $customdef = null, $customsql = null, $extra = null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: the updateCustomField() and addCustomField() methods can both be used since updateCustomField() is in fact an alias for addCustomField(). CustomFields will automatically detect if a custom field already exists, and update it if that&#039;s true, or create it.&lt;br /&gt;
&lt;br /&gt;
Note2: updating a custom field will automatically take care of the constraints and foreign keys on its own, thus you can change the foreign key, or remove it altogether if set to null.&lt;br /&gt;
&lt;br /&gt;
=== Delete a custom field definition ===&lt;br /&gt;
&lt;br /&gt;
You can easily delete a custom field by column_name or by ordinal_position:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;deleteCustomField($id);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: constraints and foreign keys will also be deleted automatically so you shouldn&#039;t encounter any error.&lt;br /&gt;
&lt;br /&gt;
=== Fetch one custom field&#039;s SQL structure ===&lt;br /&gt;
&lt;br /&gt;
To get the SQL structure of a custom field, which can be quite useful to know for example its datatype or if it has a foreign key, or accepts null values, you can use the fetchFieldStruct() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
fields = $customfields-&amp;gt;fetchFieldStruct($id=null, $nohide=false, $table=null, $cachepath=null); // where $id can be a column_name, an ordinal_position from information_schema.columns or null if you want to fetch all the columns at once&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method will either return null, either a single field object, either an array on field objects.&lt;br /&gt;
&lt;br /&gt;
Note: this function will use caching by default to accelerate future results, but you can disable it, or even change the default cache path. For more informations please read the comments in the source code.&lt;br /&gt;
&lt;br /&gt;
=== Fetch several custom fields&#039; SQL structures ===&lt;br /&gt;
&lt;br /&gt;
Use the fetchAllFieldsStruct() method, which is an alias for fetchFieldStruct():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;fetchAllFieldsStruct($nohide=false, $table=null, $cachepath=null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: this method will always return an array of fields objects, even if only one is found (for more consistency), and this is the main difference with fetchFieldStruct(null).&lt;br /&gt;
&lt;br /&gt;
== Managing custom fields records ==&lt;br /&gt;
&lt;br /&gt;
Now that you know how to create and manage the structure of custom fields, we can proceed on to managing the records.&lt;br /&gt;
&lt;br /&gt;
=== Creating a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Simply use the create() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;create($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
$object must be an object containing as its properties the customfields data.&lt;br /&gt;
&lt;br /&gt;
For example, if you have a field named mycustomfield, your object must have $object-&amp;gt;mycustomfields defined.&lt;br /&gt;
&lt;br /&gt;
This method will store in the database ALL the custom fields that were created/edited and with a valid value (eg: an empty value for a field that does not accept null value is invalid) AT ONCE! So you can modify all the columns of one record at once, but not several records at once.&lt;br /&gt;
&lt;br /&gt;
Concretely, this is very useful with Dolibarr, because modules tends to store everything inside a single object generally called $object, and CustomFields tries to hook onto $object, so that you can just pass $object as is to create() and it will save all the custom fields.&lt;br /&gt;
&lt;br /&gt;
But you can also create an empty object with $object = new stdClass(); and then manually fill the custom fields records with whatever you want, and then call the create() method, this will work as well!&lt;br /&gt;
&lt;br /&gt;
You can also just modify only one column of one record, by just specifying one custom field column_name in your $object. The other custom fields that are not specified won&#039;t be created/modified.&lt;br /&gt;
&lt;br /&gt;
=== Updating a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Use the update() method, which is an alias for the create() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;update($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As noted above, update() is an alias of the create() method, which means that CustomFields will try to detect by itself if a record needs to be updated or created.&lt;br /&gt;
&lt;br /&gt;
=== Deleting a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Use the delete() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;delete($id); // where $id is = to fk_moduleid column, NOT rowid&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fetching a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Just use the fetch() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$records = $customfields-&amp;gt;fetch($id=null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where $id can either be = fk_moduleid (NOT rowid), or null to fetch all the records at once.&lt;br /&gt;
&lt;br /&gt;
This method will either return null, a single record object, or an array of records objects.&lt;br /&gt;
&lt;br /&gt;
=== Fetching all custom fields&#039; records ===&lt;br /&gt;
&lt;br /&gt;
Use the fetchAll() method, which is an alias for fetch():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$records = $customfields-&amp;gt;fetchAll();&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: this method will always return an array of records object, for more consistency (and this is the main difference with fetch(null) ).&lt;br /&gt;
&lt;br /&gt;
=== Fetching referenced records of a constrained field ===&lt;br /&gt;
&lt;br /&gt;
A constrained custom field points to another database table, and a value for this constrained field is the rowid of a record in the referenced table.&lt;br /&gt;
&lt;br /&gt;
Thus, the principal purpose of a constrained custom field is not the value of the field in itself (it&#039;s simply an integer), but rather the other fields available from the referenced table, the constrained field being in fact just a pointer.&lt;br /&gt;
&lt;br /&gt;
To fetch the remote record from the referenced table, use the fetchReferencedValuesList() of the CustomFields class:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$fkrecord = fetchReferencedValuesList($field, $id=null, $where=null, $allcolumns=false);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
&lt;br /&gt;
* $field is a custom field object (fetched using fetchFieldStruct() or fetchAllFieldsStruct()),&lt;br /&gt;
* $id is the rowid of the record to get, just like fetch() and fetchAll(). Can be null to fetch all remote records (or to use the $where clause).&lt;br /&gt;
* $where is a string without the WHERE clause (eg: &amp;quot;fk_facture=1&amp;quot;) to get finer control. In this case, you can use $id or set $id=null depending on your purpose. This is mainly a private argument to allow for cascade option.&lt;br /&gt;
* $allcolumns if false, it will fetch only the required columns to do the smart value substitution. If true, it will fetch all remote columns (useful for ODT substitution). If an array of strings, each string will be a column to fetch from the remote database.&lt;br /&gt;
&lt;br /&gt;
Note that this function is NOT recursive (it will fetch the records from the referenced table, and that&#039;s all, it won&#039;t fetch the custom fields of the referenced table nor recursively fetch the remote constrained custom fields). To do recursive fetching, see in the Facade API.&lt;br /&gt;
&lt;br /&gt;
Note2: this function automatically manages Smart Value Substitution depending of the $field-&amp;gt;column_name.&lt;br /&gt;
&lt;br /&gt;
=== Recursive fetching referenced records of a constrained field ===&lt;br /&gt;
&lt;br /&gt;
If you want to recursively fetch a constrained field (eg: constrainedfield1-&amp;gt;ref_table-&amp;gt;constrainedfield2-&amp;gt;ref_table2-&amp;gt;...), you need another function called fetchReferencedValuesRec(). Technically, this function is in the Facade API (inside /customfields/lib/customfields_aux.lib.php), but it isn&#039;t meant to be used by lambda users (contrary to customfields_fill_object()), but is mainly a helper function for the rest of the facade API.&lt;br /&gt;
&lt;br /&gt;
fetchReferencedValuesRec() works very similarly to fetchReferencedValuesList(), but it will work recursively.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$fkrecord = fetchReferencedValuesRec($customfields, $field, $id, $recursive=true)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where $customfields is the CustomFields instance for the module you are working on, $field is the constrained field you want to fetch, $id is the record&#039;s id you want to fetch, and $recursive enables or disables the recursion.&lt;br /&gt;
&lt;br /&gt;
Note that this function automatically manages Smart Value Substitution (and also recursively).&lt;br /&gt;
&lt;br /&gt;
Note2: This function tries to automatically avoid an infinite recursion loop by using a $blacklist: for a given constrained custom field, a table won&#039;t be visited twice. This means that for example you can do: invoice-&amp;gt;user-&amp;gt;socpeople-&amp;gt;invoice, this will work and every referenced fields will be fetched, but it will automatically stop the recursion at the second invoice, because else it could continue in a loop to user, socpeople, invoice, user, socpeople, etc.&lt;br /&gt;
&lt;br /&gt;
Note3: when adding recursively referenced fields, the key in the returned array will be prefixed by the name of the custom field that led to those remote fields. Thus, you can have something like &amp;quot;cf_myfield1_cf_remotefield1_cf_remotefield2&amp;quot; if you have a constrained field cf_myfield1-&amp;gt;cf_remotefield1-&amp;gt;cf_remotefield2.&lt;br /&gt;
&lt;br /&gt;
== Input forms for custom fields ==&lt;br /&gt;
&lt;br /&gt;
The API provides some neat ways to automatically manage the input forms for users to input new values, with the best adapted HTML form based on the custom field&#039;s data type.&lt;br /&gt;
&lt;br /&gt;
The following methods will print input fields that are best adapted to the SQL data type of your custom fields as well as figure out the possible values and even format them (for example: a not null enum will be printed as a HTML select list without an empty option; fetch automatically remote columns for constrained fields and show them in a select).&lt;br /&gt;
&lt;br /&gt;
So you don&#039;t have to bother how your data will be presented to the user nor how it will be processed.&lt;br /&gt;
&lt;br /&gt;
=== Printing an input form adapted to the field&#039;s data type ===&lt;br /&gt;
&lt;br /&gt;
showInputField() is a sort of converter customfieldstructure -&amp;gt; HTML input field.&lt;br /&gt;
&lt;br /&gt;
Use the &#039;&#039;&#039;showInputField()&#039;&#039;&#039; method to print the right input form for a custom field.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;showInputField($field, $currentvalue=null, $moreparam=&#039;&#039;, $ajax_php_callback=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
showInputField() takes 4 parameters:&lt;br /&gt;
&lt;br /&gt;
* $field is a CustomFields structure object (eg: returned by fetchFieldStruct()) and is necessary to detect the right datatype and the possible values.&lt;br /&gt;
* $currentvalue is the current value if you want to preselect a value (for example if the user already defined a value, you can reload it here). This is optional, in case it is not defined, either a null/empty value will be chosen, or either the default value defined when creating the custom field.&lt;br /&gt;
* $moreparam is used to add more HTML attributes, if you want for example to add some css styling or whatever you want.&lt;br /&gt;
* $ajax_php_callback is optional and allows to specify the relative url to the php callback script. If specified, an AJAX script will be automatically attached to this field (see AJAX-PHP callback below).&lt;br /&gt;
&lt;br /&gt;
showInputField() only prints ONE HTML input field, thus you have to enclose it/them by your own HTML form.&lt;br /&gt;
&lt;br /&gt;
eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print &#039;&amp;lt;form method=&amp;quot;get&amp;quot; action=&amp;quot;?&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
print &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;customtoken&amp;quot; value=&amp;quot;ABCDEFGH&amp;quot; /&amp;gt;&#039;; // example of a custom hidden HTML input field, that can be used to pass a token for example&lt;br /&gt;
// Loop over each custom fields (you have to load them before with $customfields-&amp;gt;fetchAllFieldsStruct()&lt;br /&gt;
foreach ($customfields-&amp;gt;fields as $field) {&lt;br /&gt;
    print $customfields-&amp;gt;showInputField($field, $currentvalue=null, $moreparam=&#039;&#039;);&lt;br /&gt;
}&lt;br /&gt;
print &#039;&amp;lt;/form&amp;gt;&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can also store the resulting HTML input field and process it by a regexp for example:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$myhtmlfield = $customfields-&amp;gt;showInputField($field, $currentvalue=null, $moreparam=&#039;&#039;);&lt;br /&gt;
preg_replace($myhtmlfield, ..., ...);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: for constrained fields, if the name follows the convention of &#039;&#039;&#039;Smart Value Substitution&#039;&#039;&#039; (ie: remote columns names are included in the custom field&#039;s name), showInputField() will &#039;&#039;&#039;automatically fetch the remote values, format them, and present them&#039;&#039;&#039; in a HTML select.&lt;br /&gt;
&lt;br /&gt;
=== Printing an input form for a custom field ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;showInputForm()&#039;&#039;&#039; is a function used to print a single (and only one!) custom field with an adapted HTML input field, but enclosed by a HTML form. This is used for example when you want to allow to edit a single custom field but not the others.&lt;br /&gt;
&lt;br /&gt;
showInputForm() is in fact a wrapper for showInputField(), thus it will call showInputField() and simply enclose it by a form.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;showInputForm($id, $field, $currentvalue=null, $idvar=&#039;id&#039;, $page=null, $moreparam=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== AJAX-PHP callback ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;showInputFieldAjax()&#039;&#039;&#039; will output an AJAX script (using jQuery) to send and receive data to and from a specified php script. The AJAX script will automatically manage HTML form input&#039;s updating as long as the received data follows some convention.&lt;br /&gt;
&lt;br /&gt;
An AJAX script can be attached to any field (including non-CustomFields) by using the method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;showInputFieldAjax($id, $php_callback_url, $on_func=&amp;quot;change&amp;quot;, $request_type=&amp;quot;post&amp;quot;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
* $id is the HTML id of the field you want to attach the AJAX to.&lt;br /&gt;
* $phpcallback the relative path from Dolibarr&#039;s htdocs root folder to the php script that will receive and send back data.&lt;br /&gt;
* $on_func is the Javascript or jQuery event that should trigger the AJAX script (by default on change).&lt;br /&gt;
* $request_type is the HTML query type that will be used to send the data to the PHP callback (either &amp;quot;get&amp;quot; or &amp;quot;post&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
The AJAX script will automatically manage sending/receiving of data:&lt;br /&gt;
&lt;br /&gt;
* automatically send all form&#039;s inputs values, as well as the current calling field&#039;s name and value. The sending happens on change by default, but another event can be set using the $on_event argument. Data will be sent using standard GET or POST.&lt;br /&gt;
* Upon reception of data from the php script, the AJAX script will automatically parse the data and update the HTML fields.&lt;br /&gt;
&lt;br /&gt;
The PHP callback script must send back to AJAX the data in JSON encoded format, and with with a specific format:&lt;br /&gt;
&lt;br /&gt;
* the data must be an associative array, where each entry&#039;s key is the name (not ID!) of an HTML field to update.&lt;br /&gt;
* each entry&#039;s value is an associative array, where each sub-entry&#039;s key is the action to do. Available actions: &#039;options&#039;, &#039;value&#039;, &#039;html&#039;, &#039;alert&#039;.&lt;br /&gt;
* each sub-entry&#039;s value is the value for this action and HTML field.&lt;br /&gt;
&lt;br /&gt;
Thus, this approach allows to generically support a wide range of updating actions, managed automatically.&lt;br /&gt;
&lt;br /&gt;
You, the implementer, have just to edit the PHP callback script and return an associative array defining what action you want for each field you want to update. This is exactly what has been implemented in the Custom AJAX Functions library, which simplifies even more your job by managing automatically the step described here (generating AJAX and formatting data), so that you can focus on &#039;&#039;&#039;making the data&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Printing custom fields in documents ==&lt;br /&gt;
&lt;br /&gt;
The API can also automatically manage the printing and formatting of custom fields in your ODT, PDF and HTML documents.&lt;br /&gt;
&lt;br /&gt;
=== Printing in HTML ===&lt;br /&gt;
&lt;br /&gt;
You can either just print the value as-is, or pass them through the printField() or simpleprintField() function to format them (very useful for constrained fields) - see below.&lt;br /&gt;
&lt;br /&gt;
=== Printing in documents ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;printField()&#039;&#039;&#039; is used to properly print and translate a custom field&#039;s record&#039;s value. Generally, you can just print the custom field&#039;s value as-is, but in a few cases (eg for boolean types, or constrained fields), using the printField() method will more properly print the value.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;printField($field, $value, $outputlangs=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
* $field is the custom field&#039;s SQL structure as an object (eg: returned by fetchFieldStruct()).&lt;br /&gt;
* $value is the custom field&#039;s value for the current record (eg: $customfields-&amp;gt;fields-&amp;gt;myfield).&lt;br /&gt;
* $outputlangs is the Translate object for the target language.&lt;br /&gt;
&lt;br /&gt;
For example, with a constrained field with Smart Value Substitution, instead of printing the id like 1, it will print the full name of user like FirstName Name.&lt;br /&gt;
&lt;br /&gt;
This method will also translate a value if a translation is found in the language files.&lt;br /&gt;
&lt;br /&gt;
Another method &#039;&#039;&#039;printFieldPDF()&#039;&#039;&#039; can be used to properly print a value for a PDF (properly encoding the characters).&lt;br /&gt;
&lt;br /&gt;
Powertip: this function can also be used to get the Smart Value Substitued value of the field (instead of printing the rowid) in the field is constrained.&lt;br /&gt;
&lt;br /&gt;
=== Simple printing in documents ===&lt;br /&gt;
&lt;br /&gt;
If you don&#039;t want to have to manually fetch the structure of every custom field whose you want to print the value, you can then use a simplifier method called &#039;&#039;&#039;simpleprintField()&#039;&#039;&#039; which will take as input a custom field name instead of a custom field SQL structure object.&lt;br /&gt;
&lt;br /&gt;
This method is just a simplifier (Facade) wrapper for the printField() method, and thus will have the same advantages (fetch constrained fields, values translation, etc.).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;simpleprintField($fieldname, $value, $outputlangs=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
* $fieldname is the custom field&#039;s name (eg: &amp;quot;myfield&amp;quot;).&lt;br /&gt;
* $value is the custom field&#039;s value for the current record (eg: $customfields-&amp;gt;fields-&amp;gt;myfield).&lt;br /&gt;
* $outputlangs is the Translate object for the target language.&lt;br /&gt;
&lt;br /&gt;
Another method &#039;&#039;&#039;simpleprintFieldPDF()&#039;&#039;&#039; can be used to properly print a value for a PDF (properly encoding the characters).&lt;br /&gt;
&lt;br /&gt;
Powertip: this function can also be used to get the Smart Value Substituted value of the field (instead of printing the rowid) in the field is constrained.&lt;br /&gt;
&lt;br /&gt;
=== Find the label ===&lt;br /&gt;
&lt;br /&gt;
Finding the label of a custom field might not be as simple as it seems. First, values of properties objects may be easily used, but their name (key) is a worst job. Secondly, custom fields&#039; labels can be translated, and the following functions are expressly made for that purpose. Thirdly, for PDF and some other documents you have to encode the characters if you want them to properly be drawn.&lt;br /&gt;
&lt;br /&gt;
So if you want to print a custom field&#039;s label with translation (multilingual) support, just use the &#039;&#039;&#039;findLabel()&#039;&#039;&#039; method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$translatedfieldname = $customfields-&amp;gt;findLabel($fieldname, $outputlangs = &#039;&#039;); // where $fieldname is just a custom field label (eg: cf_mycustomfield or mycustomfield, both will work) and $outputlangs is the target language object (see the Translate class native of Dolibarr)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As for the other printing methods, you also have the &#039;&#039;&#039;findLabelPDF()&#039;&#039;&#039; wrapper method to easily print labels that with characters encoded for PDF.&lt;br /&gt;
&lt;br /&gt;
= Facade API =&lt;br /&gt;
&lt;br /&gt;
The Facade API was made to ease the usage of the CustomFields class with the Dolibarr system. It provides a neat way to do many operations with single commands, and will automatically manage the loading of the CustomFields class, and its usage with many optimizations.&lt;br /&gt;
&lt;br /&gt;
== customfields_fill_object() ==&lt;br /&gt;
&lt;br /&gt;
You can load the Facade API and then use the &#039;&#039;&#039;customfields_fill_object()&#039;&#039;&#039; to automatically fetch the fields structures and the records, and also automatically link to remote fields when a constrained field is found. This is an all-in-one solution to easily fetch CustomFields datas, and is also optimized to fully use caching and other special properties of the CustomFields class.&lt;br /&gt;
&lt;br /&gt;
Concretely, you just have to include the Facade API, then pass the customfields_fill_object() with a Dolibarr $object, and the Facade API will return a CustomFields object with appended the custom fields records and also the custom fields&#039; SQL structures.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$customfields = customfields_fill_object($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* $object will then include the custom fields records associated with this object (eg: $object-&amp;gt;customfields-&amp;gt;cf_yourfield).&lt;br /&gt;
* $customfields will be a CustomFields object containing the fields structures (eg: $customfields-&amp;gt;fields-&amp;gt;yourfield).&lt;br /&gt;
&lt;br /&gt;
You can also call any CustomFields function if you need to using the $customfields object, eg: $customfields-&amp;gt;simpleprintField($object-&amp;gt;customfields-&amp;gt;cf_yourfield);&lt;br /&gt;
&lt;br /&gt;
Note: $object can also be a dummy object containing only two properties that are required: $object-&amp;gt;id and $object-&amp;gt;table_element.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will now describe the full specification of this function:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$customfields = customfields_fill_object(&amp;amp;$object,$fromobject = null, $outputlangs = null, $prefix = null,$pdfformat = false);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* $object is the object where the customfields will be saved&lt;br /&gt;
* $fromobject is the object from where the customfields will be fetched (this can be a different module than $object, or can be null to be the same as $object)&lt;br /&gt;
* $outputlangs is the target language for translation of fields value (if empty, the default current global translation object will be used)&lt;br /&gt;
* $prefix allows you to save the custom fields in a sub-object. By default, when $prefix=null, custom fields are saved in the path $object-&amp;gt;customfields, but when you specify a prefix, custom fields will be saved inside $object-&amp;gt;customfields-&amp;gt;$prefix. This may be useful if you want to store multiple object&#039;s custom fields or records in the same $object.&lt;br /&gt;
* $pdfformat defines if you want a special formatting for the values: if null, there&#039;s no formatting, if false, there is a simple formatting (eg: translation of fields&#039; values), if true then the formatting will be adapted for PDF templating.&lt;br /&gt;
&lt;br /&gt;
For more informations on pratical usages of this function, please [[Module_CustomFields#Implementing_in_php_code_.28dolibarr_core_modules_or_your_own_module.29|read the user documentation on how to implement CustomFields in php code]] or [[Module_CustomFields_Cases#Linking_Dolibarr_objects_from_two_different_modules|read an example case of linking two objects of different modules together]].&lt;br /&gt;
&lt;br /&gt;
== customfields_fill_object_lines() ==&lt;br /&gt;
&lt;br /&gt;
Similar to customfields_fill_object() for normal custom fields, there is also a &#039;&#039;&#039;customfields_fill_object_lines()&#039;&#039;&#039; to fetch custom fields associated with lines of products/services. This function is also optimized and it is strongly advised to use it and to not try to fetch manually this kind of custom fields (ie: associated with lines of products/services rather than objects), because there are a lot of optimizations going on behind the scene and that are very hard to do manually.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields for lines of products/services (you can then access them by doing $object-&amp;gt;customfields-&amp;gt;lines-&amp;gt;$lineid-&amp;gt;cf_yourfield)&lt;br /&gt;
$customfields = customfields_fill_object_lines($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Requirements: $object can be a dummy object but must contain at least 2 properties: $object-&amp;gt;id and $object-&amp;gt;table_element_line.&lt;br /&gt;
&lt;br /&gt;
For more informations on pratical usages of this function, please [[Module_CustomFields#Implementing_in_php_code_.28dolibarr_core_modules_or_your_own_module.29|read the user documentation on how to implement CustomFields in php code]].&lt;br /&gt;
&lt;br /&gt;
= Architecture =&lt;br /&gt;
Here is a full list of the CustomFields packaged files with a short description (for a more in-depth view just crawl the source files, they are full of comments):&lt;br /&gt;
&lt;br /&gt;
== Core files ==&lt;br /&gt;
files that are necessary for the CustomFields to work, they contains the core functions&lt;br /&gt;
&lt;br /&gt;
* /customfields/admin/customfields.php --- Administrator&#039;s configuration panel : this is where you create and manage the custom fields definitions&lt;br /&gt;
* /customfields/class/actions_customfields.class.php --- Hooks class : used to hook into Dolibarr core modules without altering any core file (can be used to hook into your own modules too). Also used to preprocess the parameters before calling the printforms library.&lt;br /&gt;
* /customfields/class/customfields.class.php --- Core class : every database action is made here in this class. You can find some printing functions because they are very generic.&lt;br /&gt;
* /customfields/conf/conf_customfields.lib.php --- Configuration file : contains the main configurable variables to adapt CustomFields to your needs or to expand its support to other modules and more native sql types.&lt;br /&gt;
* /customfields/conf/conf_customfields_func.lib.php --- Auxiliary functions library to read and manage config variables&lt;br /&gt;
* /customfields/core/modules/modCustomFields.class.php --- Dolibarr&#039;s module definition file : this is a core file necessary for Dolibarr to recognize the module and to declare the hooks to Dolibarr (but it does not store anything else than meta-informations).&lt;br /&gt;
* /customfields/core/substitutions/functions_customfields.lib.php --- CustomFields substitution class for ODT generation : necessary to support customfields tags in ODT files&lt;br /&gt;
* /customfields/core/triggers/interface_50_modCustomFields_SaveFields.class.php --- Core triggers file : this is where the actions on records are managed. This is an interface between other modules and CustomFields management. You should not modify this file but you should add your triggers in $triggersarray inside conf_customfields.lib.php, but if you really have a special need and know what you do, you can edit this file to directly add your own triggers. Also, if CustomFields are shown in the forms but cannot be modified nor saved, then probably this trigger file is not detected by Dolibarr (maybe you can try to lower the number 50 to raise the priority?).&lt;br /&gt;
* /customfields/fields/customfields_fields_extend.lib.php.example --- Overloading functions library with users functions: this is where implementers can use their own php code to manage the custom fields they want (one function per action and per field).&lt;br /&gt;
* /customfields/fields/customfields_fields_ajax_custom.lib.php.example --- Custom AJAX functions library with users functions: this is where implementers can use their own php code to manage the custom fields cascading and AJAX calls, similarly to the Overloading functions library (one function per action and per field).&lt;br /&gt;
* /customfields/langs/code_CODE/customfields.lang --- Core language file : this is where you can translate the admin config panel (data types names, labels, descriptions, etc.)&lt;br /&gt;
* /customfields/langs/code_CODE/customfields-user.lang --- User defined language file : this is where you can store the labels and values of your custom fields (see the related chapter)&lt;br /&gt;
* /customfields/lib/customfields_aux.lib.php --- Simplifier library to easily use CustomFields class to populate an $object with custom fields datas (Facade design pattern). This is what you should use for most of your needs.&lt;br /&gt;
* /customfields/lib/customfields_printforms.lib.php --- Core printing library for records : contains only printing functions, there&#039;s not really any core functions inside but it allows to manage the printing of the custom fields records and their editing.&lt;br /&gt;
* /customfields/lib/customfields_ajax_wrapper.lib.php --- AJAX wrapper library to automatically manage cascading at objects creation. This isn&#039;t really necessary for CustomFields to function, this is more an addition to allow dynamical AJAX updating, but CustomFields core functionalities can work without this library.&lt;br /&gt;
* /customfields/sql/*.sql --- Unused (the tables are created directly via a function in the customfields.class.php) - but the sql files are still valid, they were used as squeletton in the first designs.&lt;br /&gt;
&lt;br /&gt;
== PDFTest module ==&lt;br /&gt;
optional module to append a page listing all custom fields values on newly generated PDF documents. To be used to test if custom fields are working correctly before making one&#039;s own template.&lt;br /&gt;
&lt;br /&gt;
* /customfieldspdftest/class/actions_customfieldspdftest.class.php --- Main functions library and hooks class, to hook onto pdf generation. Contains the PDFTest functions.&lt;br /&gt;
* /customfieldspdftest/core/modules/modCustomFieldsPDFTest.class.php --- Module descriptor for Dolibarr&lt;br /&gt;
&lt;br /&gt;
== Runtime outline ==&lt;br /&gt;
&lt;br /&gt;
Here is an outline of how CustomFields works: what functions will be called and why:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At admin page:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* No external function call (apart for some icons or needed admin functions like for tabs), everything that is done are sql queries via CustomFields class (to fetch and modify the sql columns in the customfields tables).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At printing:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Module&#039;s hook formObjectOptions calls Dolibarr&#039;s hookmanager&lt;br /&gt;
* Hookmanager calls CustomFields /class/actions_customfields.class.php, function formObjectOptions() of CustomFields&lt;br /&gt;
* Preprocess a few parameters (mainly the $action) and calls customfields_print_forms()&lt;br /&gt;
* customfields_print_forms() preprocess all the hook&#039;s parameters and assimilate them with the CustomFields&#039;s configuration parameters (there&#039;s no connection to the database at this stage, only parameters preprocessing), then calls /lib/customfields_printforms.lib.php, functions customfields_print_creation_form() or customfields_print_datasheet_form&lt;br /&gt;
* both customfields_print_creation_form() lookup the database using CustomFields class and prints empty custom fields; customfields_print_datasheet_form() lookup the database using CustomFields class, fetch the values of the custom fields and prints them;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At edition:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Same steps as &#039;&#039;&#039;At printing&#039;&#039;&#039;&lt;br /&gt;
* then customfields_print_datasheet_form() process the custom field being edited differently than the others: if editing, it prints the html input field (to enter a new value), or if saving it just saves the data into the database by calling the CUSTOMFIELDS_MODIFY trigger and then continue to print the other fields normally.&lt;br /&gt;
* CUSTOMFIELDS_MODIFY trigger calls $customfields-&amp;gt;update($newrecord);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At creation (saving):&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Module&#039;s creation trigger MYMODULE_CREATE calls Dolibarr&#039;s run_trigger()&lt;br /&gt;
* Dolibarr run_trigger() calls CustomFields trigger file /core/trigger/interface_50_modCustomFields_SaveFields.class.php, function run_trigger()&lt;br /&gt;
* CustomFields&#039;s run_trigger() assimilate the given parameters with the CustomFields&#039;s config parameters, and if the trigger is found in $triggersarray, then the appropriate action is taken (by default: _CREATE or another standard action if recognized), if not in $triggersarray, the function will still try to automatically detect the appropriate action by detecting MYMODULE_ALISTOFACTIONS with a regular expression.&lt;br /&gt;
* The appropriate action is then done (creation in this case) by calling $customfields-&amp;gt;create($record).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At edition or creation of product lines:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Similar to &#039;&#039;&#039;At edition&#039;&#039;&#039; but with hooks: formCreateProductOptions and formEditProductOptions.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At ODT document generation and substitution:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Module&#039;s ODT generator calls for Dolibarr&#039;s function complete_substitution_array(), which then calls for CustomFields substitution file /core/substitution/functions_customfields.lib.php, function customfields_completesubstitutionarray()&lt;br /&gt;
* CustomFields&#039;s function customfields_completesubstitutionarray() calls the simplifier library customfields_aux.lib.php, function customfields_fill_object() and make the substitution inside the $substitutionarray.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At ODT document generation and substitution for product lines:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Same as the previous point but this function is called for every product lines, and thus the data is cached. Also the simplifier function is here called customfields_fill_object_lines(), and the CustomFields&#039;s substitution function called customfields_completesubstitutionarray_lines().&lt;br /&gt;
&lt;br /&gt;
= How to add the support of a new module =&lt;br /&gt;
&lt;br /&gt;
There are two cases: either the module already implements hooks, and you just have to configure CustomFields to support it, or either the module does not implements hooks nor triggers.&lt;br /&gt;
&lt;br /&gt;
This chapter will guide you through the steps to make any module supported by CustomFields (be it a Dolibarr core module or your own module).&lt;br /&gt;
&lt;br /&gt;
== Preliminary test ==&lt;br /&gt;
&lt;br /&gt;
This preliminary test will allow you to know at a glance how much work needs to be done in order to make your module supported by CustomFields, and it will also give you all the config parameters if they are available.&lt;br /&gt;
&lt;br /&gt;
Important note: this test will only give data about hooks, which is the main part to make CustomFields work for your module. But for triggers, there is no way to test, so you will have to check the triggers manually (either by testing if the custom fields are saved at creation page, or by looking at the source code of the objects classes).&lt;br /&gt;
&lt;br /&gt;
Edit the php script of the module (check the URL when you access the module&#039;s datasheet or creation page - attention both are two different pages, so you have to do this twice), then put this at the very &#039;&#039;&#039;end&#039;&#039;&#039; of the php script:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// CustomFields debuglines to show config parameters for current module&lt;br /&gt;
print(&amp;quot;&amp;lt;pre&amp;gt;&amp;quot;); // html nice output&lt;br /&gt;
print(&amp;quot;table_element: {$object-&amp;gt;table_element} - table_element_line: {$object-&amp;gt;table_element_line} - contexts: &amp;quot;);&lt;br /&gt;
print_r($hookmanager-&amp;gt;contextarray);&lt;br /&gt;
print(&amp;quot; - conf hooks_modules: &amp;quot;);&lt;br /&gt;
print_r($conf-&amp;gt;hooks_modules);&lt;br /&gt;
print(&amp;quot; - hooks attached: &amp;quot;);&lt;br /&gt;
print_r($hookmanager-&amp;gt;hooks);&lt;br /&gt;
print(&amp;quot; - user rights: &amp;quot;);&lt;br /&gt;
print_r($user-&amp;gt;rights);&lt;br /&gt;
print(&amp;quot;&amp;lt;/pre&amp;gt;&amp;quot;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now reload the page, and scroll at the very bottom. You should see some informations about the module.&lt;br /&gt;
&lt;br /&gt;
Depending on the data available, you will know what you have to do:&lt;br /&gt;
&lt;br /&gt;
* If all the datas is available, then it&#039;s ok, jump to [[Module_CustomFields_Dev#Configuring_CustomFields_to_support_the_module]] and copy these datas to the parameters of conf_customfields.lib.php&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;table_element&#039;&#039;&#039;: $object is not defined, try to find how is called the main object variable and replace $object by this variable in the debug code given above (eg: in Agenda, the object variable is called $agenda and $agendacomm).&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;table_element_line&#039;&#039;&#039;: either there&#039;s no products lines for this module, either the main object variable is not called $object (see the previous point). One last case is that the object is not standard and does not store table_element_line (the name of the table that stores its products lines), in which case you have to manually add it in the class (eg: $this-&amp;gt;table_element_line = &#039;something&#039;).&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;contexts&#039;&#039;&#039;: the hookmanager is not defined, and probably there&#039;s no hook at all in this module, so you&#039;ll have to implement them by yourself. Jump to [[Module_CustomFields_Dev#Hooks]].&lt;br /&gt;
&lt;br /&gt;
* If there is a list of &#039;&#039;&#039;contexts&#039;&#039;&#039; but the module cannot be found in the list: the hookmanager is defined but the module has not defined a context, you still have to implement the hooks. Jump to [[Module_CustomFields_Dev#Hooks]].&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;conf hooks_modules&#039;&#039;&#039;: this variable is not necessary, empty or not it doesn&#039;t matter. But if you see customfields and/or customfieldspdftest inside the list, it is a confirmation that CustomFields indeed is hooking the module correctly.&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;hooks attached&#039;&#039;&#039;, or CustomFields cannot be found in the list: CustomFields has not been properly configured to hook to this module, or you forgot to disable then re enable CustomFields in order to update the CustomFields&#039;s hooks. Jump to [[Module_CustomFields_Dev#Configuring_CustomFields_to_support_the_module]].&lt;br /&gt;
&lt;br /&gt;
* If there is no &#039;&#039;&#039;$user-&amp;gt;rights&#039;&#039;&#039;: there is either no standard security implemented in this module, or either the variable is called with another name. If there is no security, you don&#039;t need to configure it in CustomFields, but if there is a security, you need to find how the variable is called (like $object can get another name sometimes).&lt;br /&gt;
&lt;br /&gt;
* If there is &#039;&#039;&#039;$user-&amp;gt;rights&#039;&#039;&#039; but the module can&#039;t be found in the list: either the name for the rights is different (this often happens, the rights array is not yet standardized), or either the rights are hardcoded into the module&#039;s code (rights should be announced to the $user-&amp;gt;rights array, but sometimes it happens that the dev forgot to do so, and thus the right is entirely hardcoded and you can&#039;t find it in the dynamic rights array). In any of these two cases, you will have to manually read the module&#039;s code to find the right. Also, maybe there is no security implemented, but since $user-&amp;gt;rights is declared this is less probable.&lt;br /&gt;
&lt;br /&gt;
Here is an example of output on a correctly hooked module (invoice):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
table_element: facture - table_element_line: facturedet - contexts: Array&lt;br /&gt;
(&lt;br /&gt;
    [0] =&amp;gt; searchform&lt;br /&gt;
    [1] =&amp;gt; leftblock&lt;br /&gt;
    [2] =&amp;gt; toprightmenu&lt;br /&gt;
    [3] =&amp;gt; invoicecard&lt;br /&gt;
)&lt;br /&gt;
 - conf hooks_modules: Array&lt;br /&gt;
(&lt;br /&gt;
    [customfields] =&amp;gt; invoicecard:propalcard:productcard:ordercard:thirdpartycard:contactcard:ordersuppliercard:invoicesuppliercard:membercard:actioncard:projectcard:projecttaskcard:contractcard:interventioncard:doncard:tripsandexpensescard:taxvatcard:expeditioncard:invoicecard:propalcard:ordercard:ordersuppliercard:invoicesuppliercard&lt;br /&gt;
    [customfieldspdftest] =&amp;gt; Array&lt;br /&gt;
        (&lt;br /&gt;
            [0] =&amp;gt; pdfgeneration&lt;br /&gt;
            [1] =&amp;gt; ordersuppliercard&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
)&lt;br /&gt;
 - hooks attached: Array&lt;br /&gt;
(&lt;br /&gt;
    [invoicecard] =&amp;gt; Array&lt;br /&gt;
        (&lt;br /&gt;
            [customfields] =&amp;gt; ActionsCustomFields Object&lt;br /&gt;
                (&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
)&lt;br /&gt;
 - user rights: stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [user] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [user] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [lire] =&amp;gt; 1&lt;br /&gt;
                    [creer] =&amp;gt; 1&lt;br /&gt;
                    [password] =&amp;gt; 1&lt;br /&gt;
                    [supprimer] =&amp;gt; 1&lt;br /&gt;
                    [export] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [self] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [creer] =&amp;gt; 1&lt;br /&gt;
                    [password] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [user_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [readperms] =&amp;gt; 1&lt;br /&gt;
                    [write] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [self_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [readperms] =&amp;gt; 1&lt;br /&gt;
                    [writeperms] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [group_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [read] =&amp;gt; 1&lt;br /&gt;
                    [readperms] =&amp;gt; 1&lt;br /&gt;
                    [write] =&amp;gt; 1&lt;br /&gt;
                    [delete] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    [facture] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [lire] =&amp;gt; 1&lt;br /&gt;
            [creer] =&amp;gt; 1&lt;br /&gt;
            [invoice_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [unvalidate] =&amp;gt; 1&lt;br /&gt;
                    [send] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [valider] =&amp;gt; 1&lt;br /&gt;
            [paiement] =&amp;gt; 1&lt;br /&gt;
            [supprimer] =&amp;gt; 1&lt;br /&gt;
            [facture] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [export] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    [propale] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [lire] =&amp;gt; 1&lt;br /&gt;
            [creer] =&amp;gt; 1&lt;br /&gt;
            [valider] =&amp;gt; 1&lt;br /&gt;
            [propal_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [send] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [cloturer] =&amp;gt; 1&lt;br /&gt;
            [supprimer] =&amp;gt; 1&lt;br /&gt;
            [export] =&amp;gt; 1&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    [produit] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [lire] =&amp;gt; 1&lt;br /&gt;
            [creer] =&amp;gt; 1&lt;br /&gt;
            [supprimer] =&amp;gt; 1&lt;br /&gt;
            [export] =&amp;gt; 1&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    etc... a lot of other rights...&lt;br /&gt;
&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When you are finished with implementing the support of CustomFields in the module, you should delete these debug lines.&lt;br /&gt;
&lt;br /&gt;
== Implementing the support of CustomFields in the module ==&lt;br /&gt;
&lt;br /&gt;
If there is a module you want to implement the support of CustomFields, but does not yet has the required components, here is a guide on how to implement these components.&lt;br /&gt;
&lt;br /&gt;
=== Hooks ===&lt;br /&gt;
&lt;br /&gt;
Implementing hooks will allow custom fields to appear on your module&#039;s page. They will also be editable and they will save the data. For creation, you need to use a trigger (see the subchapter below).&lt;br /&gt;
&lt;br /&gt;
First you should be familiar with the way that hooks are implemented in modules. If you don&#039;t, please first [[Hooks_system#Implementation|read the hooks documentation]].&lt;br /&gt;
&lt;br /&gt;
An example case of implementation of hooks into a Dolibarr core file can be found in the [[Module_CustomFields_Cases|CustomFields Example Cases wiki page]].&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Datasheet hook&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
To implement CustomFields in your module, you must initialize the hook manager, then use the following hook context:&lt;br /&gt;
* formObjectOptions&lt;br /&gt;
&lt;br /&gt;
This hook must be placed everywhere in your code where you want custom fields to appear (eg: on the creation page and datasheet view page).&lt;br /&gt;
&lt;br /&gt;
Note: Generally, you will have to place the hook just before the end of a table tag (&#039;&#039;&#039;&amp;lt;/table&amp;gt;&#039;&#039;&#039;), so if you don&#039;t know where to place it (eg: if you&#039;re implementing CustomFields on a Dolibarr core module), just search for this tag in the page shown in the url (eg: /htdocs/commande/fiche.php).&lt;br /&gt;
&lt;br /&gt;
Eg: First load the hookmanager at the beginning of your php script:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Initialize technical object to manage hooks of thirdparties. Note that conf-&amp;gt;hooks_modules contains array array&lt;br /&gt;
include_once(DOL_DOCUMENT_ROOT.&#039;/core/class/hookmanager.class.php&#039;);&lt;br /&gt;
$hookmanager=new HookManager($db);&lt;br /&gt;
$hookmanager-&amp;gt;initHooks(array(&#039;yourmodule&#039;));&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then place the hook as many times as required:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// ... a lot of php code ...&lt;br /&gt;
&lt;br /&gt;
print &#039;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&#039;; // end of the last row of a table&lt;br /&gt;
&lt;br /&gt;
// Other attributes&lt;br /&gt;
$parameters=array(&#039;colspan&#039; =&amp;gt; &#039; colspan=&amp;quot;3&amp;quot;&#039;); // specifying the colspan is optional but will help CustomFields know how to correctly print the table&lt;br /&gt;
$reshook=$hookmanager-&amp;gt;executeHooks(&#039;formObjectOptions&#039;,$parameters,$object,$action);    // Note that $action and $object may have been modified by hook&lt;br /&gt;
&lt;br /&gt;
print &#039;&amp;lt;/table&amp;gt;&#039;; // end of the table, this is what you&#039;re looking for, place the hook just above&lt;br /&gt;
&lt;br /&gt;
// ... a lot of php code ... redo the same if you find another &amp;lt;/table&amp;gt; and there&#039;s a page where there&#039;s no custom fields but you want to place custom fields on there&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note2: If after implementing the hook you receive an error or custom fields don&#039;t appear, you may have to change $object to another variable, depending on what is called the main object (eg: in Agenda it&#039;s called $action and $actioncomm).&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Lines hooks&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
For product lines, you should anyway use the product lines template files: htdocs/core/tpl/freeproductline_edit.tpl.php&lt;br /&gt;
&lt;br /&gt;
If you don&#039;t, you can still manually implement the required hooks, what is required is that you supply $line (which is the current line object being processed) in the $parameters array:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$parameters=array(&#039;line&#039;=&amp;gt;$line,&#039;fk_parent_line&#039;=&amp;gt;$line-&amp;gt;fk_parent_line);&lt;br /&gt;
&lt;br /&gt;
echo $hookmanager-&amp;gt;executeHooks(&#039;formEditProductOptions&#039;,$parameters,$this,$action);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hooks names:&lt;br /&gt;
* formEditProductOptions&lt;br /&gt;
* formCreateProductOptions ($line is not required here, because we create a new product line! you can leave $parameters=array(); empty).&lt;br /&gt;
Note: there&#039;s no hook for viewing, because showing the customfields will clutter the visual field, but if you really want you can also do it by adding a formViewProductOptions hook.&lt;br /&gt;
&lt;br /&gt;
=== Triggers ===&lt;br /&gt;
&lt;br /&gt;
First you need to know how to implement triggers in Dolibarr. For more informations about how to implement triggers in your module, please read the [[Triggers#Manage_and_create_a_new_trigger.27s_action|triggers wiki page]].&lt;br /&gt;
&lt;br /&gt;
An example case of implementation of hooks into a Dolibarr core file can be found in the [[Module_CustomFields_Cases|CustomFields Example Cases wiki page]].&lt;br /&gt;
&lt;br /&gt;
Implementing triggers on your module will allow CustomFields to save the fields at creation (datasheet edition is already managed by hooks).&lt;br /&gt;
&lt;br /&gt;
You need to implement 3 triggers:&lt;br /&gt;
- on creation of object - eg: YOURMODULE_CREATE&lt;br /&gt;
- on creation of lines - eg: YOURMODULELINE_INSERT (optional, only if your module has product lines)&lt;br /&gt;
- on update of lines - eg: YOURMODULELINE_UPDATE (optional, only if your module has product lines)&lt;br /&gt;
&lt;br /&gt;
Note: you need to supply the sql id of the newly created object before calling the trigger.&lt;br /&gt;
&lt;br /&gt;
Eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$this-&amp;gt;id = $this-&amp;gt;db-&amp;gt;last_insert_id(MAIN_DB_PREFIX.&amp;quot;yourmodule_table_element&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
// Appel des triggers&lt;br /&gt;
include_once(DOL_DOCUMENT_ROOT . &amp;quot;/core/class/interfaces.class.php&amp;quot;);&lt;br /&gt;
$interface=new Interfaces($this-&amp;gt;db);&lt;br /&gt;
$result=$interface-&amp;gt;run_triggers(&#039;YOURMODULE_CREATE&#039;,$this,$user,$langs,$conf);&lt;br /&gt;
if ($result &amp;lt; 0) {&lt;br /&gt;
    $error++; $this-&amp;gt;errors=$interface-&amp;gt;errors;&lt;br /&gt;
}&lt;br /&gt;
// Fin appel triggers&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note2: if it&#039;s a Dolibarr core module, you can easily find the right place to put the trigger by searching for &#039;create sql&#039; in the class subfolder (eg: URL is /htdocs/commande/fiche.php, the class is in /htdocs/commande/class/commande.class.php).&lt;br /&gt;
&lt;br /&gt;
Note3: CustomFields also supports a few other triggers like _CLONE, _UPDATE, _MODIFY, but they are not required and may not work depending on how your module works to manage these actions.&lt;br /&gt;
&lt;br /&gt;
== Configuring CustomFields to support the module ==&lt;br /&gt;
&lt;br /&gt;
=== Preliminary work ===&lt;br /&gt;
&lt;br /&gt;
You need to get the parameters of this module, and you will then put them in the CustomFields configuration file (conf_customfields.lib.php).&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Getting the parameters automatically&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
You can use the [[Module_CustomFields_Dev#Preliminary_test|preliminary test]] to easily get all those parameters.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Getting the parameters manually&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
To configure CustomFields to support a module, you need at least 5 parameters:&lt;br /&gt;
* the module&#039;s table_name (name of the sql table where the module stores its datas)&lt;br /&gt;
* the module&#039;s context (name given by the module to the hookmanager)&lt;br /&gt;
* the module&#039;s idvar (name of the variable that holds the sql row id of the record)&lt;br /&gt;
* the module&#039;s trigger action(s) (triggers on creation and update, see the related chapter above)&lt;br /&gt;
* the module&#039;s rights (for security purposes)&lt;br /&gt;
&lt;br /&gt;
To get these parameters manually, you can take a look inside the code of the module&#039;s files you want to implement:&lt;br /&gt;
* module&#039;s table name: either find it by yourself in the database (looks like llx_themodulename) or by printing it in actions_customfields.class.php by adding &#039;&#039;&#039;print_r($parameters)&#039;&#039;&#039; and search for $parameters-&amp;gt;table_element&lt;br /&gt;
* module&#039;s context: search for &amp;quot;callHooks(&amp;quot; without the quotes or take a look at the wiki: http://wiki.dolibarr.org/index.php/Hooks_system&lt;br /&gt;
or you can get it by printing it in the /htdocs/customfields/class/actions_customfields.class.php by adding print_r($parameters) and search for $parameters-&amp;gt;context&lt;br /&gt;
* module&#039;s trigger actions: search for &amp;quot;run_triggers(&amp;quot; or take a look at the wiki: http://wiki.dolibarr.org/index.php/Triggers#List_of_known_triggers_actions&lt;br /&gt;
* module&#039;s idvar: look at the url when you access one of the module&#039;s record, and you should see something like fiche.php?xxxid=12 - you need the xxxid (eg: socid, facid, etc.).&lt;br /&gt;
* module&#039;s security: in the page shown in URL when accessing the module&#039;s datasheet, edit this file and add &#039;&#039;&#039;print($user-&amp;gt;rights)&#039;&#039;&#039;, then in the list try to find the correct right (generally something like $user-&amp;gt;modulename-&amp;gt;creer). You can also try to search for $user-&amp;gt;rights in the sourcecode of this module.&lt;br /&gt;
&lt;br /&gt;
With these values, edit the config file (/htdocs/customfields/conf/conf_customfields.lib.php), particularly the $modulesarray and $triggersarray variables:&lt;br /&gt;
&lt;br /&gt;
=== Configure hooks and main parameters ===&lt;br /&gt;
&lt;br /&gt;
Add every parameters except triggers in the $modulesarray:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$modulesarray = array(&lt;br /&gt;
                      array(&#039;context&#039;=&amp;gt;&#039;invoicecard&#039;, &#039;table_element&#039;=&amp;gt;&#039;facture&#039;, &#039;idvar&#039;=&amp;gt;&#039;facid&#039;, &#039;rights&#039;=&amp;gt;array(&#039;$user-&amp;gt;rights-&amp;gt;facture-&amp;gt;creer&#039;)), // Client Invoice&lt;br /&gt;
                      array(&#039;context&#039;=&amp;gt;&#039;membercard&#039;, &#039;table_element&#039;=&amp;gt;&#039;adherent&#039;, &#039;idvar&#039;=&amp;gt;&#039;rowid&#039;, &#039;rights&#039;=&amp;gt;array(&#039;$user-&amp;gt;rights-&amp;gt;adherent-&amp;gt;creer&#039;), &#039;tabs&#039;=&amp;gt;array(&#039;objecttype&#039;=&amp;gt;&#039;member_admin&#039;, &#039;function&#039;=&amp;gt;&#039;member_admin_prepare_head&#039;, &#039;lib&#039;=&amp;gt;DOL_DOCUMENT_ROOT.&#039;/core/lib/member.lib.php&#039;)), // Members / Adherents&lt;br /&gt;
                     ); // Edit me to add the support of another module - NOTE: Lowercase only!&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are also a lot of others parameters you can give to CustomFields so that the module gets better managed or to add a few other functionnalities, here is a non-exhaustive list:&lt;br /&gt;
* context: name of the module&#039;s context.&lt;br /&gt;
* table_element: name of the sql table that holds the object&#039;s datas.&lt;br /&gt;
* idvar: name of the variable that holds the id of the object in the sql database (by default &#039;id&#039; or &#039;rowid&#039;).&lt;br /&gt;
* rights: prevent the custom fields to be editable unless all the rights set here are enabled (preventing custom fields to be viewed is useless because they won&#039;t show anyway if the page can&#039;t be loaded due to insufficient rights, so this is directly handled by the module).&lt;br /&gt;
* tabs=&amp;gt;array(objecttype, function, lib, tabname, tabtitle): used to embed CustomFields&#039;s admin panel into another module&#039;s admin page.&lt;br /&gt;
&lt;br /&gt;
=== Configure creation triggers ===&lt;br /&gt;
&lt;br /&gt;
Add the triggers in $triggersarray:&lt;br /&gt;
$triggersarray = array(&amp;quot;order_create&amp;quot;=&amp;gt;&amp;quot;commande&amp;quot;,&lt;br /&gt;
                       &amp;quot;yourmodule_triggeraction1&amp;quot;=&amp;gt;&amp;quot;yourmodule_tablename&amp;quot;,&lt;br /&gt;
                       &amp;quot;yourmodule_triggeraction2&amp;quot;=&amp;gt;&amp;quot;yourmodule_tablename&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
Note: generally you will be looking to implement the _CREATE.&lt;br /&gt;
&lt;br /&gt;
Note2: if a triggeraction is not recognized by CustomFields (eg: mymodule_addpayment) then by default CustomFields will assimilate it to a _create action (it will create and save the values for the custom fields, just for a module&#039;s creation page).&lt;br /&gt;
&lt;br /&gt;
Note3: CustomFields also supports a few other triggers like _CLONE, _UPDATE, _MODIFY, but they are not required and may not work depending on how your module works to manage these actions.&lt;br /&gt;
&lt;br /&gt;
=== Configuration troubleshooting ===&lt;br /&gt;
&lt;br /&gt;
* If you cannot see the custom fields appear on your module&#039;s page, this means the hooks are not implemented or configured in CustomFields, please read the related chapters.&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet but there&#039;s no edit button icon: the module implements security but you did not configure it properly in CustomFields, please find the right security in $user-&amp;gt;rights and set it in conf_customfields.lib.php.&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the creation page but are not saved: you forgot to set the trigger in $triggersarray, or the creation trigger does not exist for this module and you have to implement it. Jump to [[Module_CustomFields_Dev#Triggers]].&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet but not on the creation page (or vice versa): A hook is missing on the page where the custom fields do not appear. Jump to [[Module_CustomFields_Dev#Hooks]].&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet with the edit button, but when the edit button is clicked, everything disappear: either the $idvar is not properly set (look at the URL to know if you need to set idvar), or the hook is not properly made ($object is not defined and it&#039;s called by another name, but the hook use $object instead of the correct variable).&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet with the edit button, can be edited but cannot be saved (the data is not saved): same as the previous point: either $idvar or $object bug.&lt;br /&gt;
&lt;br /&gt;
Also you might find useful to &#039;&#039;&#039;[[Module_CustomFields#General_advices_for_troubleshooting|enable php errors]]&#039;&#039;&#039; to better track errors if that&#039;s not already done.&lt;br /&gt;
&lt;br /&gt;
= Example cases =&lt;br /&gt;
&lt;br /&gt;
You can find a compilation of concrete, practical examples on the [[Module_CustomFields_Cases]] wiki page.&lt;br /&gt;
&lt;br /&gt;
= TO DO =&lt;br /&gt;
&lt;br /&gt;
You will find below the public list of features that should be implemented in the future.&lt;br /&gt;
&lt;br /&gt;
If you have an idea about a new feature that isn&#039;t listed below, feel free to add it to the list!&lt;br /&gt;
&lt;br /&gt;
== To Do ==&lt;br /&gt;
Nothing here!&lt;br /&gt;
&lt;br /&gt;
== To Document ==&lt;br /&gt;
* Automatic recopy on cloning for custom fields and products&#039; lines custom fields.&lt;br /&gt;
* new API function: customfields_clone_or_recopy($object, $fromobject, $action2 = null)&lt;br /&gt;
&lt;br /&gt;
== Should do ==&lt;br /&gt;
* Add the possibility to mark a custom field to be shown in tables listings (eg: as a column in products listings) with &amp;quot;order by this column&amp;quot; buttons and stuffs. Will be difficult if the hooks are not yet implemented in listings.&lt;br /&gt;
* Checkbox (multiple checkboxes, one for each possible value) and multiselect (big select and can use SHIFT to multiselect) (store in XML file) (but impossible with constrained fields, can&#039;t store multiple foreign keys ids in one single sql field, but would work for other types like enum)&lt;br /&gt;
use fieldset, input checkbox and label for, eg:&lt;br /&gt;
    &amp;lt;fieldset&amp;gt;&amp;lt;legend&amp;gt;&amp;lt;label for=&amp;quot;cf_productslist&amp;quot;&amp;gt; $langs-&amp;gt;trans(&#039;cf_productslist&#039;)&amp;lt;/label&amp;gt;&amp;lt;/legend&amp;gt;&lt;br /&gt;
    &amp;lt;input type=&amp;quot;checkbox&amp;quot; name=&amp;quot;cf_productslist_choice1&amp;quot; value=&amp;quot;1&amp;quot; id=&amp;quot;cf_productslist_choice1&amp;quot; /&amp;gt;&amp;lt;label for=&amp;quot;cf_productslist_choice1&amp;quot;&amp;gt; $langs-&amp;gt;trans(&#039;cf_productslist_choice1&#039;)&amp;lt;/label&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
    &amp;lt;input type=&amp;quot;checkbox&amp;quot; name=&amp;quot;cf_productslist_choice2&amp;quot; value=&amp;quot;1&amp;quot; id=&amp;quot;cf_productslist_choice2&amp;quot; /&amp;gt;&amp;lt;label for=&amp;quot;cf_productslist_choice2&amp;quot;&amp;gt; $langs-&amp;gt;trans(&#039;cf_productslist_choice2&#039;)&amp;lt;/label&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
    etc...&lt;br /&gt;
    &amp;lt;/fieldset&amp;gt;&lt;br /&gt;
* Add a javascript options values generator for the enum type (a hidden input that would be shown only when DropdownBox type is selected and that would permit to add options by clicking a plus button).&lt;br /&gt;
* Replace triggers in customfields class by hookmanager?&lt;br /&gt;
* AJAX previsualisation of the HTML custom field that will be created.&lt;br /&gt;
* In ODTs, create special tags to directly access day, month or year value of a date type field independently of the rest.&lt;br /&gt;
* New overloading function&#039;s action: &amp;quot;get&amp;quot;, similar to view except that it works not only on Dolibarr&#039;s view but whenever someone tries to access this field&#039;s value.&lt;br /&gt;
* Implementation of custom fields in CSV exports (may need a few new hooks in Dolibarr?).&lt;br /&gt;
* Add Upload and Image field types (this would allow for an easier integration inside ODT and PDF templates than the current workflow of Dolibarr).&lt;br /&gt;
&lt;br /&gt;
== Known bugs ==&lt;br /&gt;
* in product and service modules, if you edit a field, the proposals and other fields below won&#039;t be shown, you need to refresh the page. This problem resides in Dolibarr I think (since we are simply using a hook).&lt;br /&gt;
* ficheinter (Intervention module) custom fields will stay in create mode just after create page is submitted (this is because of the $action that is not standard - $action=edit or something).&lt;br /&gt;
&lt;br /&gt;
== Never/Maybe one day ==&lt;br /&gt;
* Add support for repeatable (predefined) invoices (the way it is currently managed makes it very difficult to manage this without making a big exception, adding specific functions in customfields modules that would not at all will be reusable anywhere else, when customfields has been designed to be as generic as possible to support any module and any version of dolibarr, because it&#039;s managed by a totally different table while it&#039;s still managed by the same module, CustomFields work with the paradigm: one module, one table).&lt;br /&gt;
* Add an AJAX select box for constrained values : when a constrained type is selected and a table is selected, a hidden select box would show up with the list of the fields of this table to choose the values that will be printed as the values for this customfield (eg: for table llx_users you could select the &amp;quot;nom&amp;quot; field and then it would automatically prepend &amp;quot;nom_&amp;quot; to the field&#039;s name).&lt;br /&gt;
* Fine-grained rights management: rights per group, per user, and per field.&lt;br /&gt;
* Replace overloading functions (extend class) by hookmanager if possible (but how to simulate the *full switch? Plus this will force users to make their own modules to make those functions, and to disable/renable their modules everytime they will add a new function...).&lt;br /&gt;
* Refactor the trigger array: merge it with the modulesarray (along with a new way to specify which customfields trigger action a trigger should correspond, eg: &#039;linebill_insert&#039;=&amp;gt;&#039;customfields_create&#039;). But the triggers aren&#039;t necessarily associated with a specific module...&lt;/div&gt;</summary>
		<author><name>Lrq3000</name></author>
	</entry>
	<entry>
		<id>https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Dev&amp;diff=33135</id>
		<title>Module CustomFields Dev</title>
		<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Dev&amp;diff=33135"/>
		<updated>2015-02-03T22:38:58Z</updated>

		<summary type="html">&lt;p&gt;Lrq3000: /* Printing in documents */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:CustomFields]]&lt;br /&gt;
{{TemplateDocDevEn}}&lt;br /&gt;
{{TemplateModEN}}&lt;br /&gt;
{{BasculeDevUserEn|&lt;br /&gt;
name=CustomFields|&lt;br /&gt;
num=8500|&lt;br /&gt;
devdoc=This page|&lt;br /&gt;
userdoc=[[Module_CustomFields]]|}}&lt;br /&gt;
[[Category:FAQ EN]]&lt;br /&gt;
&lt;br /&gt;
These notes are aimed to developpers who want to extend the functionnalities of the module or want to use it for extreme cases.&lt;br /&gt;
This is not meant to guide implementers on how to implement and configure the module (see the related chapter for that).&lt;br /&gt;
&lt;br /&gt;
If you are an implementer and just want to use CustomFields with its normal functions, you can read the [[Module_CustomFields|user documentation of CustomFields]].&lt;br /&gt;
&lt;br /&gt;
You can also jump directly to [[Module_CustomFields_Cases]] wiki page for a collection of practical examples.&lt;br /&gt;
&lt;br /&gt;
= Principles =&lt;br /&gt;
&lt;br /&gt;
CustomFields is simply a wrapper for SQL databases.&lt;br /&gt;
&lt;br /&gt;
The module is based on SQL to manage the fields and most of the stuff. It uses and understands standard SQL.&lt;br /&gt;
&lt;br /&gt;
The main principles of CustomFields are:&lt;br /&gt;
&lt;br /&gt;
* one SQL column = one Dolibarr field.&lt;br /&gt;
* one SQL type = one method to show and edit the field in Dolibarr.&lt;br /&gt;
* almost everything should be configurable from the configuration file, or admin interface.&lt;br /&gt;
* be independent from Dolibarr&#039;s data structures.&lt;br /&gt;
&lt;br /&gt;
CustomFields sits besides Dolibarr, and does not mix with it, even if in the Dolibarr&#039;s interface it looks like CustomFields is embedded inside Dolibarr, in reality, CustomFields is totally independent.&lt;br /&gt;
&lt;br /&gt;
= Goals =&lt;br /&gt;
&lt;br /&gt;
This module was made to provide an easy way to manage custom sql fields, not only for users and implementers, but also for developpers to use in their own module:&lt;br /&gt;
* by easily adding the support of custom fields for your own module by using this module;&lt;br /&gt;
* by reusing custom fields from another module in your own module;&lt;br /&gt;
* by managing sql fields in your own module by using the CustomFields class (which will take care of everything from printing, editing to PDF/ODT templating).&lt;br /&gt;
&lt;br /&gt;
So this module is not only an auxiliary of other modules, but it can also be the core part of your own module by using it as your SQL query class for fields.&lt;br /&gt;
&lt;br /&gt;
= SQL structure =&lt;br /&gt;
&lt;br /&gt;
A CustomFields table needs to respect three parameters that never change:&lt;br /&gt;
&lt;br /&gt;
* table&#039;s name (format: llx_*dolibarrmodule*_customfields where *dolibarrmodule* = table_element in conf_customfields.lib.php)&lt;br /&gt;
* first column: rowid, which is the primary key for each field&lt;br /&gt;
* second column: fk_*dolibarrmodule* which is the foreign key that links every customfields record to the Dolibarr&#039;s module that is associated (invoice, order, etc...).&lt;br /&gt;
&lt;br /&gt;
Apart from the table&#039;s name and the first two special columns, every other columns are just standard sql columns that you can freely create, edit or delete as you want, from the module&#039;s administration page or from a third-party SQL management tool (such as phpMyAdmin).&lt;br /&gt;
&lt;br /&gt;
You can also create more complex sql fields: constraints, foreign keys, transformations, other sql types, etc...).&lt;br /&gt;
&lt;br /&gt;
Then the module will try to manage the sql fields you manually created the best it can:&lt;br /&gt;
* by printing appropriate input fields in the Dolibarr gui interface.&lt;br /&gt;
* by providing the developper an easy access to these fields in php (eg: $customfields-&amp;gt;cf_myfield or $object-&amp;gt;customfields-&amp;gt;cf_myfield) for any use, including PDF templates and ODT templates, or in any php script (so that you can use these fields in your own module).&lt;br /&gt;
&lt;br /&gt;
= SQL Indexes =&lt;br /&gt;
&lt;br /&gt;
The module automatically adds an index on the primary key and foreign key (first and second columns).&lt;br /&gt;
&lt;br /&gt;
To further optimize the performances of your database, and particularly the customfields tables, you may want to manually add indexes based on usage.&lt;br /&gt;
&lt;br /&gt;
Also, you may add indexes on remote tables used by constrained fields, which could optimize even more the performances since constrained fields have the worst performance drawback for CustomFields.&lt;br /&gt;
&lt;br /&gt;
= SQL Compatibility mode =&lt;br /&gt;
&lt;br /&gt;
The module relies on referential integrity checks and foreign keys of your DBMS to keep the database clean and to provide the constrained fields feature. Referential integrity checks is part of the SQL standards and should be used whenever possible, which always was a main goal for CustomFields (to be respectful of SQL standards).&lt;br /&gt;
&lt;br /&gt;
Unluckily, a few DBMS systems still don&#039;t support this critical functionality of the SQL standard, for example MySQL enables MyISAM engine by default, which doesn&#039;t support this feature.&lt;br /&gt;
&lt;br /&gt;
In these cases, you can either do two things:&lt;br /&gt;
* Switch to an engine which support referential integrity. For example with MySQL, you can enable InnoDB, which support referential integrity checks and foreign keys.&lt;br /&gt;
* Use CustomFields compatibility mode (more infos below).&lt;br /&gt;
&lt;br /&gt;
In the case your database does not support referential integrity checks, CustomFields will automatically switch to the SQL compatibility mode, which will allow you to use CustomFields even on databases where foreign keys are not possible.&lt;br /&gt;
&lt;br /&gt;
In compatibility mode, you will get nearly all CustomFields advanced features such as Constrained fields, but the customfields&#039; tables in the database won&#039;t be cleaned automatically (no delete on cascade, meaning that when you delete an object, the linked custom fields will still be kept in your database), and constrained fields won&#039;t be updated automatically on update.&lt;br /&gt;
&lt;br /&gt;
Nevertheless, these missing cleanup functionalities won&#039;t be seen by your users because they will never be accessible: on deletion of an object, the custom fields won&#039;t ever be accessible again from Dolibarr&#039;s interface, and object&#039;s ids are never changed since Dolibarr also relies on this. Thus in compatibility mode, CustomFields will act just like normally, but your database will be a bit less clean.&lt;br /&gt;
&lt;br /&gt;
= JSON extension for extra options =&lt;br /&gt;
&lt;br /&gt;
== Introduction to JSON in CustomFields ==&lt;br /&gt;
The relational model is very great to store and fetch data with a declarative language, and this is very powerful, but this model also imposes a few limitations, for example the relations must be well formatted and predefined.&lt;br /&gt;
&lt;br /&gt;
That&#039;s why we finally implemented a &#039;&#039;&#039;JSON extension&#039;&#039;&#039; to manage &#039;&#039;&#039;extra options&#039;&#039;&#039; and associate them with custom fields. This allows to add many properties (or columns if you are more used to the relational model) on-the-fly without having to predefine them prior to storing and using them.&lt;br /&gt;
&lt;br /&gt;
This allows to add an undefined number of columns/properties/extra options that will help manage special behaviours for custom fields. For example, it is currently used to allow to change the order of appearance of custom fields, and to store and manage remote table and columns datas for constrained fields on DBMS that do not support Foreign Keys checks.&lt;br /&gt;
&lt;br /&gt;
We chose JSON over XML because the library are simpler and the data footprint smaller, and because CustomFields does not need advanced features of XML like XQuery, data typing and meta structures (at least not for now).&lt;br /&gt;
&lt;br /&gt;
The other advantage is that php objects can directly be encoded into JSON without any preprocessing, and can then as straightly be decoded from JSON back into a php object.&lt;br /&gt;
&lt;br /&gt;
A simple mean to encode and decode back an object into and from a storage place, as well as allow users to easily add/remove extra options were the main requirement, and JSON (+ JSON libraries for PHP) meet this criteria.&lt;br /&gt;
&lt;br /&gt;
However, note that the extra options are now encoded as an associative array, so as to avoid any ambiguities (there&#039;s no associative array in JSON, they are converted to objects).&lt;br /&gt;
&lt;br /&gt;
== Implementation of JSON ==&lt;br /&gt;
The implementation is very simple: a table called &#039;&#039;&#039;llx_customfields_extraoptions&#039;&#039;&#039; is created when you enable the CustomFields module, and this table only contains three columns:&lt;br /&gt;
* table_name (varchar)&lt;br /&gt;
* column_name (varchar)&lt;br /&gt;
* extraoptions (blob)&lt;br /&gt;
&lt;br /&gt;
(table_name, column_name) both forms the primary key of the table, and links to columns (custom fields) of other customfields tables.&lt;br /&gt;
extraoptions is a blob, which allows to store the data as binary (as-is) without encoding (which solves a big problem of JSON which is encoding special and foreign characters, here there is no problem at all).&lt;br /&gt;
&lt;br /&gt;
When fetching custom fields, the module simply JOIN the extraoptions table, which adds an &#039;&#039;&#039;extraoptions&#039;&#039;&#039; column for each custom field in the SQL result. Then it is quite easy to just json_decode() the data returned inside the extraoptions column.&lt;br /&gt;
&lt;br /&gt;
This methodology allows to have all the advantages of a relational database, with the flexibility of a free-schema model:&lt;br /&gt;
* everything is stored inside the database (only one storage place)&lt;br /&gt;
* fixed columns allows for relational joining and optimization&lt;br /&gt;
* thus queries (fetching) are optimised&lt;br /&gt;
* at the same time, the extraoptions are automatically retrieved and associated with every custom field, and whenever needed they can be used&lt;br /&gt;
* and thus we also get a flexible model to &#039;&#039;&#039;add extra (virtual) columns whenever needed&#039;&#039;&#039; without having to alter the relational database schema&lt;br /&gt;
&lt;br /&gt;
Thus no critical information is and should be stored inside the extraoptions column because they would not be query-able, only extra options that are just used to tweak the management of those fields inside the PHP application should be stored. But this is a wonderful way to add non critical informations that still allows to develop more complex features and management for special custom fields.&lt;br /&gt;
&lt;br /&gt;
== CustomFields JSON API ==&lt;br /&gt;
&lt;br /&gt;
=== Storing extra options ===&lt;br /&gt;
&lt;br /&gt;
You can use the &#039;&#039;&#039;setExtra()&#039;&#039;&#039; method to set extra options of a custom field.&lt;br /&gt;
&lt;br /&gt;
Eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$extra = array(); // $extra should always be an associative array, it&#039;s no longer an object&lt;br /&gt;
$extra[&#039;myproperty&#039;] = &#039;Anything I want here&#039;; // set a property/extra option like this&lt;br /&gt;
$extra[&#039;category&#039;][&#039;subcategory&#039;][&#039;prop&#039;] = true; // we can set recursive properties, there&#039;s not any limit to how deep the hierarchy can be&lt;br /&gt;
&lt;br /&gt;
require_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomField($db, &#039;currentmodule&#039;);&lt;br /&gt;
$fieldid = &#039;mycustomfields&#039;; // $fieldid can either be a column_name or an ordinal_position from information_schema.columns&lt;br /&gt;
$result = $customfields-&amp;gt;setExtra($fieldid, $extra); // store the extra options inside the database&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can also use addCustomField() or updateCustomField() with an $extra array, and these functions will also call the setExtra() method.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: the extra options you set are &#039;&#039;&#039;appended&#039;&#039;&#039; to the ones already stored in the database. However, you can also overwrite previous options, just use the same name of property but with a different value (the replacement is recursive, you can do it at any level in the hierarchy).&lt;br /&gt;
&lt;br /&gt;
=== Fetching extra options ===&lt;br /&gt;
&lt;br /&gt;
Extra options are automatically fetched along with a custom field structure, decoded, and stored inside the extra sub-property of any field object, or CustomFields object.&lt;br /&gt;
&lt;br /&gt;
When fetched, extra options are stored as a sub-object for the current field object (which itself is stored as a subobject of the CustomFields object if caching is enabled).&lt;br /&gt;
&lt;br /&gt;
Eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
require_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomField($db, &#039;currentmodule&#039;);&lt;br /&gt;
$fields = fetchAllFieldsStruct(); // fetching fields structure will automatically fetch extra options at the same time. This works with fetchAllFieldsStruct() and fetchFieldStruct()&lt;br /&gt;
&lt;br /&gt;
// Now that you fetched the fields structure, you can just access the extra options sub-properties:&lt;br /&gt;
print_r($fields-&amp;gt;mycustomfield-&amp;gt;extra); // will print all extra options for the field mycustomfield&lt;br /&gt;
print_r($customfields-&amp;gt;fields-&amp;gt;mycustomfield-&amp;gt;extra); // you can also access the same field(s) directly from the CustomFields object if caching is enabled (default behaviour)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Modifying extra options ===&lt;br /&gt;
&lt;br /&gt;
You can just combine the two previous points to edit extra options of a custom field: just load a custom field, edit on-the-fly the extra property with PHP code, and then store it back directly into the database:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
require_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomField($db, &#039;currentmodule&#039;);&lt;br /&gt;
$fields = fetchAllFieldsStruct();&lt;br /&gt;
&lt;br /&gt;
$fields-&amp;gt;mycustomfield-&amp;gt;extra[&#039;newproperty&#039;] = &#039;This is a new property&#039;; // add a new property over the old extra options sub-object&lt;br /&gt;
$fields-&amp;gt;mycustomfield-&amp;gt;extra[&#039;mycategory&#039;] = &#039;Overwrite the old category&#039;; // this will replace the previous value of &#039;mycategory&#039; extra option&lt;br /&gt;
&lt;br /&gt;
$customfields-&amp;gt;setExtra(&#039;mycustomfield&#039;, $fields-&amp;gt;mycustomfield-&amp;gt;extra); // store the modified extra options back into the database for future usage&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= CustomFields API =&lt;br /&gt;
&lt;br /&gt;
== Introduction to CustomFields API ==&lt;br /&gt;
&lt;br /&gt;
This chapter will cover the basics on how to manually and concretely use the CustomFields class to create/update/fetch/manage custom fields.&lt;br /&gt;
&lt;br /&gt;
This will give you a better understanding of how CustomFields works, as well as giving you insights on how to use it for your own applications.&lt;br /&gt;
&lt;br /&gt;
Important note: contrary to the Facade API, the core CustomFields class use columns names without any prefix, so don&#039;t try to use the prefix, it won&#039;t work! For example: if your field is named &#039;mycustomfield&#039; and prefix is &#039;cf_&#039;, when you use the Facade API you should use &#039;$object-&amp;gt;$path-&amp;gt;cf_mycustomfield&#039; but when you use the core CustomFields class directly, $fieldname = &#039;mycustomfield&#039;.&lt;br /&gt;
&lt;br /&gt;
== Prerequisite to use the CustomFields class ==&lt;br /&gt;
&lt;br /&gt;
Before using the CustomFields class, you first must instanciate it. This is a necessary preliminary step before using any of the methods that will be covered inside this chapter, and thus will not be repeated, only shown once here.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Include the CustomFields class (which contains the main API)&lt;br /&gt;
require_once(dirname(__FILE__).&#039;/../class/customfields.class.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Instanciate a CustomFields object&lt;br /&gt;
global $db; // the database handler created by Dolibarr&lt;br /&gt;
$table_element = &#039;currentmodule&#039;; // where $table_element is the database&#039;s table name for the module, without the Dolibarr prefix (eg: for &#039;llx_facture&#039;, table_element is &#039;facture&#039;). You can also find it inside the $object properties (just print_r() it).&lt;br /&gt;
$customfields = new CustomFields($db, $table_element); // instanciate the CustomFields class inside $customfields object&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
CustomFields keeps track of errors that can happen when executing methods and SQL queries inside the &#039;&#039;&#039;error&#039;&#039;&#039; and &#039;&#039;&#039;errors&#039;&#039;&#039; properties, which you can print with the printErrors() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Do something with CustomFields before&lt;br /&gt;
&lt;br /&gt;
if (count($customfields-&amp;gt;errors) &amp;gt; 0) { // If there is at least one error&lt;br /&gt;
    $customfields-&amp;gt;printErrors(); // print errors&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// You can also print your own error message, in two manners:&lt;br /&gt;
&lt;br /&gt;
// 1- submit your own message&lt;br /&gt;
$customfields-&amp;gt;printErrors(&#039;Your own error message here&#039;); // this will print ONLY your error message, using the Dolibarr&#039;s AJAX error dialog (standard way of printing errors)&lt;br /&gt;
&lt;br /&gt;
// 2- append your message in the customfields errors&lt;br /&gt;
$customfields-&amp;gt;errors[] = &#039;Your own error message to be appended with other CustomFields messages&#039;;&lt;br /&gt;
$customfields-&amp;gt;printErrors(); // Here your error message will be appended, thus all error messages will be printed. This allows you for example to add more intelligible informations on a specific error.&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== General purpose SQL methods ==&lt;br /&gt;
&lt;br /&gt;
CustomFields provide an interface to easily execute various common SQL requests, as well as your own custom SQL requests.&lt;br /&gt;
&lt;br /&gt;
* fetchAllTables() fetch a list of all the tables in the database&lt;br /&gt;
* fetchPrimaryField($table) fetch the column_name (and only the column_name!) of the primary field of a table. You can then use fetchFieldStruct()  with the column_name to get any information you want.&lt;br /&gt;
* fetchAny($columns, $table, $where=&#039;&#039;, $orderby=&#039;&#039;, $limitby=&#039;&#039;) allows you to issue any simple SQL query command, for example fetchAny(&#039;*&#039;, &#039;sometable&#039;) is equivalent to SELECT * FROM sometable; (but then everything is managed automatically and result is returned to you)&lt;br /&gt;
* executeSQL($sql, $eventname) to execute any SQL query you want, this will return you a resource (and NOT a processed nuplet like fetchAny!). $eventname can be anything, it&#039;s just for Dolibarr logging facility.&lt;br /&gt;
* executeMultiSQL($sql, $eventname) to execute multiple SQL queries&lt;br /&gt;
* fetchFieldStruct($id=null, $nohide=false, $table=null, $cachepath=null) allows you to fetch the field&#039;s SQL structure of not only custom fields, but of any database&#039;s column using the $table variable. Eg: fetchFieldStruct(&#039;mycolumn&#039;, true, &#039;mytable&#039;);&lt;br /&gt;
* probeTable($table) helps you to check if a table exists in the database&lt;br /&gt;
* fetch_object($resource) will return a PHP object from a $resource, similarly to mysql_fetch_object() but with two advantages: it works on several DBMS (not only mysql), and it always return lowercase properties names (else depending on the configuration of the DBMS, the request can return uppercase or mixed case properties names).&lt;br /&gt;
* escape() and reverse_escape() are similare to mysql_real_escape but is reversible and DBMS-independent (so you can use it with other DBMSes).&lt;br /&gt;
&lt;br /&gt;
== Managing CustomFields structure ==&lt;br /&gt;
&lt;br /&gt;
The first step into CustomFields is to create and manage the structure of a custom field.&lt;br /&gt;
&lt;br /&gt;
=== Creating a custom field definition ===&lt;br /&gt;
&lt;br /&gt;
To create a custom field, you can use the addCustomField() method.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;addCustomField($fieldname, $type, $size, $nulloption, $defaultvalue = null, $constraint = null, $customtype = null, $customdef = null, $customsql = null, $fieldid = null, $extra = null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will take care of forging the (complicated) SQL query with the forgeSQLCustomField() method, and will return you an error if there was a problem.&lt;br /&gt;
&lt;br /&gt;
=== Updating a custom field definition ===&lt;br /&gt;
&lt;br /&gt;
To update a custom field, you can use the updateCustomField() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;&lt;br /&gt;
updateCustomField($fieldid, $fieldname, $type, $size, $nulloption, $defaultvalue, $constraint = null, $customtype = null, $customdef = null, $customsql = null, $extra = null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: the updateCustomField() and addCustomField() methods can both be used since updateCustomField() is in fact an alias for addCustomField(). CustomFields will automatically detect if a custom field already exists, and update it if that&#039;s true, or create it.&lt;br /&gt;
&lt;br /&gt;
Note2: updating a custom field will automatically take care of the constraints and foreign keys on its own, thus you can change the foreign key, or remove it altogether if set to null.&lt;br /&gt;
&lt;br /&gt;
=== Delete a custom field definition ===&lt;br /&gt;
&lt;br /&gt;
You can easily delete a custom field by column_name or by ordinal_position:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;deleteCustomField($id);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: constraints and foreign keys will also be deleted automatically so you shouldn&#039;t encounter any error.&lt;br /&gt;
&lt;br /&gt;
=== Fetch one custom field&#039;s SQL structure ===&lt;br /&gt;
&lt;br /&gt;
To get the SQL structure of a custom field, which can be quite useful to know for example its datatype or if it has a foreign key, or accepts null values, you can use the fetchFieldStruct() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
fields = $customfields-&amp;gt;fetchFieldStruct($id=null, $nohide=false, $table=null, $cachepath=null); // where $id can be a column_name, an ordinal_position from information_schema.columns or null if you want to fetch all the columns at once&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method will either return null, either a single field object, either an array on field objects.&lt;br /&gt;
&lt;br /&gt;
Note: this function will use caching by default to accelerate future results, but you can disable it, or even change the default cache path. For more informations please read the comments in the source code.&lt;br /&gt;
&lt;br /&gt;
=== Fetch several custom fields&#039; SQL structures ===&lt;br /&gt;
&lt;br /&gt;
Use the fetchAllFieldsStruct() method, which is an alias for fetchFieldStruct():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;fetchAllFieldsStruct($nohide=false, $table=null, $cachepath=null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: this method will always return an array of fields objects, even if only one is found (for more consistency), and this is the main difference with fetchFieldStruct(null).&lt;br /&gt;
&lt;br /&gt;
== Managing custom fields records ==&lt;br /&gt;
&lt;br /&gt;
Now that you know how to create and manage the structure of custom fields, we can proceed on to managing the records.&lt;br /&gt;
&lt;br /&gt;
=== Creating a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Simply use the create() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;create($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
$object must be an object containing as its properties the customfields data.&lt;br /&gt;
&lt;br /&gt;
For example, if you have a field named mycustomfield, your object must have $object-&amp;gt;mycustomfields defined.&lt;br /&gt;
&lt;br /&gt;
This method will store in the database ALL the custom fields that were created/edited and with a valid value (eg: an empty value for a field that does not accept null value is invalid) AT ONCE! So you can modify all the columns of one record at once, but not several records at once.&lt;br /&gt;
&lt;br /&gt;
Concretely, this is very useful with Dolibarr, because modules tends to store everything inside a single object generally called $object, and CustomFields tries to hook onto $object, so that you can just pass $object as is to create() and it will save all the custom fields.&lt;br /&gt;
&lt;br /&gt;
But you can also create an empty object with $object = new stdClass(); and then manually fill the custom fields records with whatever you want, and then call the create() method, this will work as well!&lt;br /&gt;
&lt;br /&gt;
You can also just modify only one column of one record, by just specifying one custom field column_name in your $object. The other custom fields that are not specified won&#039;t be created/modified.&lt;br /&gt;
&lt;br /&gt;
=== Updating a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Use the update() method, which is an alias for the create() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;update($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As noted above, update() is an alias of the create() method, which means that CustomFields will try to detect by itself if a record needs to be updated or created.&lt;br /&gt;
&lt;br /&gt;
=== Deleting a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Use the delete() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;delete($id); // where $id is = to fk_moduleid column, NOT rowid&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fetching a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Just use the fetch() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$records = $customfields-&amp;gt;fetch($id=null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where $id can either be = fk_moduleid (NOT rowid), or null to fetch all the records at once.&lt;br /&gt;
&lt;br /&gt;
This method will either return null, a single record object, or an array of records objects.&lt;br /&gt;
&lt;br /&gt;
=== Fetching all custom fields&#039; records ===&lt;br /&gt;
&lt;br /&gt;
Use the fetchAll() method, which is an alias for fetch():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$records = $customfields-&amp;gt;fetchAll();&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: this method will always return an array of records object, for more consistency (and this is the main difference with fetch(null) ).&lt;br /&gt;
&lt;br /&gt;
=== Fetching referenced records of a constrained field ===&lt;br /&gt;
&lt;br /&gt;
A constrained custom field points to another database table, and a value for this constrained field is the rowid of a record in the referenced table.&lt;br /&gt;
&lt;br /&gt;
Thus, the principal purpose of a constrained custom field is not the value of the field in itself (it&#039;s simply an integer), but rather the other fields available from the referenced table, the constrained field being in fact just a pointer.&lt;br /&gt;
&lt;br /&gt;
To fetch the remote record from the referenced table, use the fetchReferencedValuesList() of the CustomFields class:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$fkrecord = fetchReferencedValuesList($field, $id=null, $where=null, $allcolumns=false);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
&lt;br /&gt;
* $field is a custom field object (fetched using fetchFieldStruct() or fetchAllFieldsStruct()),&lt;br /&gt;
* $id is the rowid of the record to get, just like fetch() and fetchAll(). Can be null to fetch all remote records (or to use the $where clause).&lt;br /&gt;
* $where is a string without the WHERE clause (eg: &amp;quot;fk_facture=1&amp;quot;) to get finer control. In this case, you can use $id or set $id=null depending on your purpose. This is mainly a private argument to allow for cascade option.&lt;br /&gt;
* $allcolumns if false, it will fetch only the required columns to do the smart value substitution. If true, it will fetch all remote columns (useful for ODT substitution). If an array of strings, each string will be a column to fetch from the remote database.&lt;br /&gt;
&lt;br /&gt;
Note that this function is NOT recursive (it will fetch the records from the referenced table, and that&#039;s all, it won&#039;t fetch the custom fields of the referenced table nor recursively fetch the remote constrained custom fields). To do recursive fetching, see in the Facade API.&lt;br /&gt;
&lt;br /&gt;
Note2: this function automatically manages Smart Value Substitution depending of the $field-&amp;gt;column_name.&lt;br /&gt;
&lt;br /&gt;
=== Recursive fetching referenced records of a constrained field ===&lt;br /&gt;
&lt;br /&gt;
If you want to recursively fetch a constrained field (eg: constrainedfield1-&amp;gt;ref_table-&amp;gt;constrainedfield2-&amp;gt;ref_table2-&amp;gt;...), you need another function called fetchReferencedValuesRec(). Technically, this function is in the Facade API (inside /customfields/lib/customfields_aux.lib.php), but it isn&#039;t meant to be used by lambda users (contrary to customfields_fill_object()), but is mainly a helper function for the rest of the facade API.&lt;br /&gt;
&lt;br /&gt;
fetchReferencedValuesRec() works very similarly to fetchReferencedValuesList(), but it will work recursively.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$fkrecord = fetchReferencedValuesRec($customfields, $field, $id, $recursive=true)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where $customfields is the CustomFields instance for the module you are working on, $field is the constrained field you want to fetch, $id is the record&#039;s id you want to fetch, and $recursive enables or disables the recursion.&lt;br /&gt;
&lt;br /&gt;
Note that this function automatically manages Smart Value Substitution (and also recursively).&lt;br /&gt;
&lt;br /&gt;
Note2: This function tries to automatically avoid an infinite recursion loop by using a $blacklist: for a given constrained custom field, a table won&#039;t be visited twice. This means that for example you can do: invoice-&amp;gt;user-&amp;gt;socpeople-&amp;gt;invoice, this will work and every referenced fields will be fetched, but it will automatically stop the recursion at the second invoice, because else it could continue in a loop to user, socpeople, invoice, user, socpeople, etc.&lt;br /&gt;
&lt;br /&gt;
Note3: when adding recursively referenced fields, the key in the returned array will be prefixed by the name of the custom field that led to those remote fields. Thus, you can have something like &amp;quot;cf_myfield1_cf_remotefield1_cf_remotefield2&amp;quot; if you have a constrained field cf_myfield1-&amp;gt;cf_remotefield1-&amp;gt;cf_remotefield2.&lt;br /&gt;
&lt;br /&gt;
== Input forms for custom fields ==&lt;br /&gt;
&lt;br /&gt;
The API provides some neat ways to automatically manage the input forms for users to input new values, with the best adapted HTML form based on the custom field&#039;s data type.&lt;br /&gt;
&lt;br /&gt;
The following methods will print input fields that are best adapted to the SQL data type of your custom fields as well as figure out the possible values and even format them (for example: a not null enum will be printed as a HTML select list without an empty option; fetch automatically remote columns for constrained fields and show them in a select).&lt;br /&gt;
&lt;br /&gt;
So you don&#039;t have to bother how your data will be presented to the user nor how it will be processed.&lt;br /&gt;
&lt;br /&gt;
=== Printing an input form adapted to the field&#039;s data type ===&lt;br /&gt;
&lt;br /&gt;
showInputField() is a sort of converter customfieldstructure -&amp;gt; HTML input field.&lt;br /&gt;
&lt;br /&gt;
Use the &#039;&#039;&#039;showInputField()&#039;&#039;&#039; method to print the right input form for a custom field.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;showInputField($field, $currentvalue=null, $moreparam=&#039;&#039;, $ajax_php_callback=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
showInputField() takes 4 parameters:&lt;br /&gt;
&lt;br /&gt;
* $field is a CustomFields structure object (eg: returned by fetchFieldStruct()) and is necessary to detect the right datatype and the possible values.&lt;br /&gt;
* $currentvalue is the current value if you want to preselect a value (for example if the user already defined a value, you can reload it here). This is optional, in case it is not defined, either a null/empty value will be chosen, or either the default value defined when creating the custom field.&lt;br /&gt;
* $moreparam is used to add more HTML attributes, if you want for example to add some css styling or whatever you want.&lt;br /&gt;
* $ajax_php_callback is optional and allows to specify the relative url to the php callback script. If specified, an AJAX script will be automatically attached to this field (see AJAX-PHP callback below).&lt;br /&gt;
&lt;br /&gt;
showInputField() only prints ONE HTML input field, thus you have to enclose it/them by your own HTML form.&lt;br /&gt;
&lt;br /&gt;
eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print &#039;&amp;lt;form method=&amp;quot;get&amp;quot; action=&amp;quot;?&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
print &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;customtoken&amp;quot; value=&amp;quot;ABCDEFGH&amp;quot; /&amp;gt;&#039;; // example of a custom hidden HTML input field, that can be used to pass a token for example&lt;br /&gt;
// Loop over each custom fields (you have to load them before with $customfields-&amp;gt;fetchAllFieldsStruct()&lt;br /&gt;
foreach ($customfields-&amp;gt;fields as $field) {&lt;br /&gt;
    print $customfields-&amp;gt;showInputField($field, $currentvalue=null, $moreparam=&#039;&#039;);&lt;br /&gt;
}&lt;br /&gt;
print &#039;&amp;lt;/form&amp;gt;&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can also store the resulting HTML input field and process it by a regexp for example:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$myhtmlfield = $customfields-&amp;gt;showInputField($field, $currentvalue=null, $moreparam=&#039;&#039;);&lt;br /&gt;
preg_replace($myhtmlfield, ..., ...);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: for constrained fields, if the name follows the convention of &#039;&#039;&#039;Smart Value Substitution&#039;&#039;&#039; (ie: remote columns names are included in the custom field&#039;s name), showInputField() will &#039;&#039;&#039;automatically fetch the remote values, format them, and present them&#039;&#039;&#039; in a HTML select.&lt;br /&gt;
&lt;br /&gt;
=== Printing an input form for a custom field ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;showInputForm()&#039;&#039;&#039; is a function used to print a single (and only one!) custom field with an adapted HTML input field, but enclosed by a HTML form. This is used for example when you want to allow to edit a single custom field but not the others.&lt;br /&gt;
&lt;br /&gt;
showInputForm() is in fact a wrapper for showInputField(), thus it will call showInputField() and simply enclose it by a form.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;showInputForm($id, $field, $currentvalue=null, $idvar=&#039;id&#039;, $page=null, $moreparam=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== AJAX-PHP callback ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;showInputFieldAjax()&#039;&#039;&#039; will output an AJAX script (using jQuery) to send and receive data to and from a specified php script. The AJAX script will automatically manage HTML form input&#039;s updating as long as the received data follows some convention.&lt;br /&gt;
&lt;br /&gt;
An AJAX script can be attached to any field (including non-CustomFields) by using the method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;showInputFieldAjax($id, $php_callback_url, $on_func=&amp;quot;change&amp;quot;, $request_type=&amp;quot;post&amp;quot;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
* $id is the HTML id of the field you want to attach the AJAX to.&lt;br /&gt;
* $phpcallback the relative path from Dolibarr&#039;s htdocs root folder to the php script that will receive and send back data.&lt;br /&gt;
* $on_func is the Javascript or jQuery event that should trigger the AJAX script (by default on change).&lt;br /&gt;
* $request_type is the HTML query type that will be used to send the data to the PHP callback (either &amp;quot;get&amp;quot; or &amp;quot;post&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
The AJAX script will automatically manage sending/receiving of data:&lt;br /&gt;
&lt;br /&gt;
* automatically send all form&#039;s inputs values, as well as the current calling field&#039;s name and value. The sending happens on change by default, but another event can be set using the $on_event argument. Data will be sent using standard GET or POST.&lt;br /&gt;
* Upon reception of data from the php script, the AJAX script will automatically parse the data and update the HTML fields.&lt;br /&gt;
&lt;br /&gt;
The PHP callback script must send back to AJAX the data in JSON encoded format, and with with a specific format:&lt;br /&gt;
&lt;br /&gt;
* the data must be an associative array, where each entry&#039;s key is the name (not ID!) of an HTML field to update.&lt;br /&gt;
* each entry&#039;s value is an associative array, where each sub-entry&#039;s key is the action to do. Available actions: &#039;options&#039;, &#039;value&#039;, &#039;html&#039;, &#039;alert&#039;.&lt;br /&gt;
* each sub-entry&#039;s value is the value for this action and HTML field.&lt;br /&gt;
&lt;br /&gt;
Thus, this approach allows to generically support a wide range of updating actions, managed automatically.&lt;br /&gt;
&lt;br /&gt;
You, the implementer, have just to edit the PHP callback script and return an associative array defining what action you want for each field you want to update. This is exactly what has been implemented in the Custom AJAX Functions library, which simplifies even more your job by managing automatically the step described here (generating AJAX and formatting data), so that you can focus on &#039;&#039;&#039;making the data&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Printing custom fields in documents ==&lt;br /&gt;
&lt;br /&gt;
The API can also automatically manage the printing and formatting of custom fields in your ODT, PDF and HTML documents.&lt;br /&gt;
&lt;br /&gt;
=== Printing in HTML ===&lt;br /&gt;
&lt;br /&gt;
You can either just print the value as-is, or pass them through the printField() or simpleprintField() function to format them (very useful for constrained fields) - see below.&lt;br /&gt;
&lt;br /&gt;
=== Printing in documents ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;printField()&#039;&#039;&#039; is used to properly print and translate a custom field&#039;s record&#039;s value. Generally, you can just print the custom field&#039;s value as-is, but in a few cases (eg for boolean types, or constrained fields), using the printField() method will more properly print the value.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;printField($field, $value, $outputlangs=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
* $field is the custom field&#039;s SQL structure as an object (eg: returned by fetchFieldStruct()).&lt;br /&gt;
* $value is the custom field&#039;s value for the current record (eg: $customfields-&amp;gt;fields-&amp;gt;myfield).&lt;br /&gt;
* $outputlangs is the Translate object for the target language.&lt;br /&gt;
&lt;br /&gt;
For example, with a constrained field with Smart Value Substitution, instead of printing the id like 1, it will print the full name of user like FirstName Name.&lt;br /&gt;
&lt;br /&gt;
This method will also translate a value if a translation is found in the language files.&lt;br /&gt;
&lt;br /&gt;
Another method &#039;&#039;&#039;printFieldPDF()&#039;&#039;&#039; can be used to properly print a value for a PDF (properly encoding the characters).&lt;br /&gt;
&lt;br /&gt;
Powertip: this function can also be used to get the Smart Value Substitued value of the field (instead of printing the rowid) in the field is constrained.&lt;br /&gt;
&lt;br /&gt;
=== Simple printing in documents ===&lt;br /&gt;
&lt;br /&gt;
If you don&#039;t want to have to manually fetch the structure of every custom field whose you want to print the value, you can then use a simplifier method called &#039;&#039;&#039;simpleprintField()&#039;&#039;&#039; which will take as input a custom field name instead of a custom field SQL structure object.&lt;br /&gt;
&lt;br /&gt;
This method is just a simplifier (Facade) wrapper for the printField() method, and thus will have the same advantages (fetch constrained fields, values translation, etc.).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;simpleprintField($fieldname, $value, $outputlangs=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
* $fieldname is the custom field&#039;s name (eg: &amp;quot;myfield&amp;quot;).&lt;br /&gt;
* $value is the custom field&#039;s value for the current record (eg: $customfields-&amp;gt;fields-&amp;gt;myfield).&lt;br /&gt;
* $outputlangs is the Translate object for the target language.&lt;br /&gt;
&lt;br /&gt;
Another method &#039;&#039;&#039;simpleprintFieldPDF()&#039;&#039;&#039; can be used to properly print a value for a PDF (properly encoding the characters).&lt;br /&gt;
&lt;br /&gt;
=== Find the label ===&lt;br /&gt;
&lt;br /&gt;
Finding the label of a custom field might not be as simple as it seems. First, values of properties objects may be easily used, but their name (key) is a worst job. Secondly, custom fields&#039; labels can be translated, and the following functions are expressly made for that purpose. Thirdly, for PDF and some other documents you have to encode the characters if you want them to properly be drawn.&lt;br /&gt;
&lt;br /&gt;
So if you want to print a custom field&#039;s label with translation (multilingual) support, just use the &#039;&#039;&#039;findLabel()&#039;&#039;&#039; method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$translatedfieldname = $customfields-&amp;gt;findLabel($fieldname, $outputlangs = &#039;&#039;); // where $fieldname is just a custom field label (eg: cf_mycustomfield or mycustomfield, both will work) and $outputlangs is the target language object (see the Translate class native of Dolibarr)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As for the other printing methods, you also have the &#039;&#039;&#039;findLabelPDF()&#039;&#039;&#039; wrapper method to easily print labels that with characters encoded for PDF.&lt;br /&gt;
&lt;br /&gt;
= Facade API =&lt;br /&gt;
&lt;br /&gt;
The Facade API was made to ease the usage of the CustomFields class with the Dolibarr system. It provides a neat way to do many operations with single commands, and will automatically manage the loading of the CustomFields class, and its usage with many optimizations.&lt;br /&gt;
&lt;br /&gt;
== customfields_fill_object() ==&lt;br /&gt;
&lt;br /&gt;
You can load the Facade API and then use the &#039;&#039;&#039;customfields_fill_object()&#039;&#039;&#039; to automatically fetch the fields structures and the records, and also automatically link to remote fields when a constrained field is found. This is an all-in-one solution to easily fetch CustomFields datas, and is also optimized to fully use caching and other special properties of the CustomFields class.&lt;br /&gt;
&lt;br /&gt;
Concretely, you just have to include the Facade API, then pass the customfields_fill_object() with a Dolibarr $object, and the Facade API will return a CustomFields object with appended the custom fields records and also the custom fields&#039; SQL structures.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$customfields = customfields_fill_object($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* $object will then include the custom fields records associated with this object (eg: $object-&amp;gt;customfields-&amp;gt;cf_yourfield).&lt;br /&gt;
* $customfields will be a CustomFields object containing the fields structures (eg: $customfields-&amp;gt;fields-&amp;gt;yourfield).&lt;br /&gt;
&lt;br /&gt;
You can also call any CustomFields function if you need to using the $customfields object, eg: $customfields-&amp;gt;simpleprintField($object-&amp;gt;customfields-&amp;gt;cf_yourfield);&lt;br /&gt;
&lt;br /&gt;
Note: $object can also be a dummy object containing only two properties that are required: $object-&amp;gt;id and $object-&amp;gt;table_element.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will now describe the full specification of this function:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$customfields = customfields_fill_object(&amp;amp;$object,$fromobject = null, $outputlangs = null, $prefix = null,$pdfformat = false);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* $object is the object where the customfields will be saved&lt;br /&gt;
* $fromobject is the object from where the customfields will be fetched (this can be a different module than $object, or can be null to be the same as $object)&lt;br /&gt;
* $outputlangs is the target language for translation of fields value (if empty, the default current global translation object will be used)&lt;br /&gt;
* $prefix allows you to save the custom fields in a sub-object. By default, when $prefix=null, custom fields are saved in the path $object-&amp;gt;customfields, but when you specify a prefix, custom fields will be saved inside $object-&amp;gt;customfields-&amp;gt;$prefix. This may be useful if you want to store multiple object&#039;s custom fields or records in the same $object.&lt;br /&gt;
* $pdfformat defines if you want a special formatting for the values: if null, there&#039;s no formatting, if false, there is a simple formatting (eg: translation of fields&#039; values), if true then the formatting will be adapted for PDF templating.&lt;br /&gt;
&lt;br /&gt;
For more informations on pratical usages of this function, please [[Module_CustomFields#Implementing_in_php_code_.28dolibarr_core_modules_or_your_own_module.29|read the user documentation on how to implement CustomFields in php code]] or [[Module_CustomFields_Cases#Linking_Dolibarr_objects_from_two_different_modules|read an example case of linking two objects of different modules together]].&lt;br /&gt;
&lt;br /&gt;
== customfields_fill_object_lines() ==&lt;br /&gt;
&lt;br /&gt;
Similar to customfields_fill_object() for normal custom fields, there is also a &#039;&#039;&#039;customfields_fill_object_lines()&#039;&#039;&#039; to fetch custom fields associated with lines of products/services. This function is also optimized and it is strongly advised to use it and to not try to fetch manually this kind of custom fields (ie: associated with lines of products/services rather than objects), because there are a lot of optimizations going on behind the scene and that are very hard to do manually.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields for lines of products/services (you can then access them by doing $object-&amp;gt;customfields-&amp;gt;lines-&amp;gt;$lineid-&amp;gt;cf_yourfield)&lt;br /&gt;
$customfields = customfields_fill_object_lines($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Requirements: $object can be a dummy object but must contain at least 2 properties: $object-&amp;gt;id and $object-&amp;gt;table_element_line.&lt;br /&gt;
&lt;br /&gt;
For more informations on pratical usages of this function, please [[Module_CustomFields#Implementing_in_php_code_.28dolibarr_core_modules_or_your_own_module.29|read the user documentation on how to implement CustomFields in php code]].&lt;br /&gt;
&lt;br /&gt;
= Architecture =&lt;br /&gt;
Here is a full list of the CustomFields packaged files with a short description (for a more in-depth view just crawl the source files, they are full of comments):&lt;br /&gt;
&lt;br /&gt;
== Core files ==&lt;br /&gt;
files that are necessary for the CustomFields to work, they contains the core functions&lt;br /&gt;
&lt;br /&gt;
* /customfields/admin/customfields.php --- Administrator&#039;s configuration panel : this is where you create and manage the custom fields definitions&lt;br /&gt;
* /customfields/class/actions_customfields.class.php --- Hooks class : used to hook into Dolibarr core modules without altering any core file (can be used to hook into your own modules too). Also used to preprocess the parameters before calling the printforms library.&lt;br /&gt;
* /customfields/class/customfields.class.php --- Core class : every database action is made here in this class. You can find some printing functions because they are very generic.&lt;br /&gt;
* /customfields/conf/conf_customfields.lib.php --- Configuration file : contains the main configurable variables to adapt CustomFields to your needs or to expand its support to other modules and more native sql types.&lt;br /&gt;
* /customfields/conf/conf_customfields_func.lib.php --- Auxiliary functions library to read and manage config variables&lt;br /&gt;
* /customfields/core/modules/modCustomFields.class.php --- Dolibarr&#039;s module definition file : this is a core file necessary for Dolibarr to recognize the module and to declare the hooks to Dolibarr (but it does not store anything else than meta-informations).&lt;br /&gt;
* /customfields/core/substitutions/functions_customfields.lib.php --- CustomFields substitution class for ODT generation : necessary to support customfields tags in ODT files&lt;br /&gt;
* /customfields/core/triggers/interface_50_modCustomFields_SaveFields.class.php --- Core triggers file : this is where the actions on records are managed. This is an interface between other modules and CustomFields management. You should not modify this file but you should add your triggers in $triggersarray inside conf_customfields.lib.php, but if you really have a special need and know what you do, you can edit this file to directly add your own triggers. Also, if CustomFields are shown in the forms but cannot be modified nor saved, then probably this trigger file is not detected by Dolibarr (maybe you can try to lower the number 50 to raise the priority?).&lt;br /&gt;
* /customfields/fields/customfields_fields_extend.lib.php.example --- Overloading functions library with users functions: this is where implementers can use their own php code to manage the custom fields they want (one function per action and per field).&lt;br /&gt;
* /customfields/fields/customfields_fields_ajax_custom.lib.php.example --- Custom AJAX functions library with users functions: this is where implementers can use their own php code to manage the custom fields cascading and AJAX calls, similarly to the Overloading functions library (one function per action and per field).&lt;br /&gt;
* /customfields/langs/code_CODE/customfields.lang --- Core language file : this is where you can translate the admin config panel (data types names, labels, descriptions, etc.)&lt;br /&gt;
* /customfields/langs/code_CODE/customfields-user.lang --- User defined language file : this is where you can store the labels and values of your custom fields (see the related chapter)&lt;br /&gt;
* /customfields/lib/customfields_aux.lib.php --- Simplifier library to easily use CustomFields class to populate an $object with custom fields datas (Facade design pattern). This is what you should use for most of your needs.&lt;br /&gt;
* /customfields/lib/customfields_printforms.lib.php --- Core printing library for records : contains only printing functions, there&#039;s not really any core functions inside but it allows to manage the printing of the custom fields records and their editing.&lt;br /&gt;
* /customfields/lib/customfields_ajax_wrapper.lib.php --- AJAX wrapper library to automatically manage cascading at objects creation. This isn&#039;t really necessary for CustomFields to function, this is more an addition to allow dynamical AJAX updating, but CustomFields core functionalities can work without this library.&lt;br /&gt;
* /customfields/sql/*.sql --- Unused (the tables are created directly via a function in the customfields.class.php) - but the sql files are still valid, they were used as squeletton in the first designs.&lt;br /&gt;
&lt;br /&gt;
== PDFTest module ==&lt;br /&gt;
optional module to append a page listing all custom fields values on newly generated PDF documents. To be used to test if custom fields are working correctly before making one&#039;s own template.&lt;br /&gt;
&lt;br /&gt;
* /customfieldspdftest/class/actions_customfieldspdftest.class.php --- Main functions library and hooks class, to hook onto pdf generation. Contains the PDFTest functions.&lt;br /&gt;
* /customfieldspdftest/core/modules/modCustomFieldsPDFTest.class.php --- Module descriptor for Dolibarr&lt;br /&gt;
&lt;br /&gt;
== Runtime outline ==&lt;br /&gt;
&lt;br /&gt;
Here is an outline of how CustomFields works: what functions will be called and why:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At admin page:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* No external function call (apart for some icons or needed admin functions like for tabs), everything that is done are sql queries via CustomFields class (to fetch and modify the sql columns in the customfields tables).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At printing:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Module&#039;s hook formObjectOptions calls Dolibarr&#039;s hookmanager&lt;br /&gt;
* Hookmanager calls CustomFields /class/actions_customfields.class.php, function formObjectOptions() of CustomFields&lt;br /&gt;
* Preprocess a few parameters (mainly the $action) and calls customfields_print_forms()&lt;br /&gt;
* customfields_print_forms() preprocess all the hook&#039;s parameters and assimilate them with the CustomFields&#039;s configuration parameters (there&#039;s no connection to the database at this stage, only parameters preprocessing), then calls /lib/customfields_printforms.lib.php, functions customfields_print_creation_form() or customfields_print_datasheet_form&lt;br /&gt;
* both customfields_print_creation_form() lookup the database using CustomFields class and prints empty custom fields; customfields_print_datasheet_form() lookup the database using CustomFields class, fetch the values of the custom fields and prints them;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At edition:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Same steps as &#039;&#039;&#039;At printing&#039;&#039;&#039;&lt;br /&gt;
* then customfields_print_datasheet_form() process the custom field being edited differently than the others: if editing, it prints the html input field (to enter a new value), or if saving it just saves the data into the database by calling the CUSTOMFIELDS_MODIFY trigger and then continue to print the other fields normally.&lt;br /&gt;
* CUSTOMFIELDS_MODIFY trigger calls $customfields-&amp;gt;update($newrecord);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At creation (saving):&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Module&#039;s creation trigger MYMODULE_CREATE calls Dolibarr&#039;s run_trigger()&lt;br /&gt;
* Dolibarr run_trigger() calls CustomFields trigger file /core/trigger/interface_50_modCustomFields_SaveFields.class.php, function run_trigger()&lt;br /&gt;
* CustomFields&#039;s run_trigger() assimilate the given parameters with the CustomFields&#039;s config parameters, and if the trigger is found in $triggersarray, then the appropriate action is taken (by default: _CREATE or another standard action if recognized), if not in $triggersarray, the function will still try to automatically detect the appropriate action by detecting MYMODULE_ALISTOFACTIONS with a regular expression.&lt;br /&gt;
* The appropriate action is then done (creation in this case) by calling $customfields-&amp;gt;create($record).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At edition or creation of product lines:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Similar to &#039;&#039;&#039;At edition&#039;&#039;&#039; but with hooks: formCreateProductOptions and formEditProductOptions.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At ODT document generation and substitution:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Module&#039;s ODT generator calls for Dolibarr&#039;s function complete_substitution_array(), which then calls for CustomFields substitution file /core/substitution/functions_customfields.lib.php, function customfields_completesubstitutionarray()&lt;br /&gt;
* CustomFields&#039;s function customfields_completesubstitutionarray() calls the simplifier library customfields_aux.lib.php, function customfields_fill_object() and make the substitution inside the $substitutionarray.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At ODT document generation and substitution for product lines:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Same as the previous point but this function is called for every product lines, and thus the data is cached. Also the simplifier function is here called customfields_fill_object_lines(), and the CustomFields&#039;s substitution function called customfields_completesubstitutionarray_lines().&lt;br /&gt;
&lt;br /&gt;
= How to add the support of a new module =&lt;br /&gt;
&lt;br /&gt;
There are two cases: either the module already implements hooks, and you just have to configure CustomFields to support it, or either the module does not implements hooks nor triggers.&lt;br /&gt;
&lt;br /&gt;
This chapter will guide you through the steps to make any module supported by CustomFields (be it a Dolibarr core module or your own module).&lt;br /&gt;
&lt;br /&gt;
== Preliminary test ==&lt;br /&gt;
&lt;br /&gt;
This preliminary test will allow you to know at a glance how much work needs to be done in order to make your module supported by CustomFields, and it will also give you all the config parameters if they are available.&lt;br /&gt;
&lt;br /&gt;
Important note: this test will only give data about hooks, which is the main part to make CustomFields work for your module. But for triggers, there is no way to test, so you will have to check the triggers manually (either by testing if the custom fields are saved at creation page, or by looking at the source code of the objects classes).&lt;br /&gt;
&lt;br /&gt;
Edit the php script of the module (check the URL when you access the module&#039;s datasheet or creation page - attention both are two different pages, so you have to do this twice), then put this at the very &#039;&#039;&#039;end&#039;&#039;&#039; of the php script:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// CustomFields debuglines to show config parameters for current module&lt;br /&gt;
print(&amp;quot;&amp;lt;pre&amp;gt;&amp;quot;); // html nice output&lt;br /&gt;
print(&amp;quot;table_element: {$object-&amp;gt;table_element} - table_element_line: {$object-&amp;gt;table_element_line} - contexts: &amp;quot;);&lt;br /&gt;
print_r($hookmanager-&amp;gt;contextarray);&lt;br /&gt;
print(&amp;quot; - conf hooks_modules: &amp;quot;);&lt;br /&gt;
print_r($conf-&amp;gt;hooks_modules);&lt;br /&gt;
print(&amp;quot; - hooks attached: &amp;quot;);&lt;br /&gt;
print_r($hookmanager-&amp;gt;hooks);&lt;br /&gt;
print(&amp;quot; - user rights: &amp;quot;);&lt;br /&gt;
print_r($user-&amp;gt;rights);&lt;br /&gt;
print(&amp;quot;&amp;lt;/pre&amp;gt;&amp;quot;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now reload the page, and scroll at the very bottom. You should see some informations about the module.&lt;br /&gt;
&lt;br /&gt;
Depending on the data available, you will know what you have to do:&lt;br /&gt;
&lt;br /&gt;
* If all the datas is available, then it&#039;s ok, jump to [[Module_CustomFields_Dev#Configuring_CustomFields_to_support_the_module]] and copy these datas to the parameters of conf_customfields.lib.php&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;table_element&#039;&#039;&#039;: $object is not defined, try to find how is called the main object variable and replace $object by this variable in the debug code given above (eg: in Agenda, the object variable is called $agenda and $agendacomm).&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;table_element_line&#039;&#039;&#039;: either there&#039;s no products lines for this module, either the main object variable is not called $object (see the previous point). One last case is that the object is not standard and does not store table_element_line (the name of the table that stores its products lines), in which case you have to manually add it in the class (eg: $this-&amp;gt;table_element_line = &#039;something&#039;).&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;contexts&#039;&#039;&#039;: the hookmanager is not defined, and probably there&#039;s no hook at all in this module, so you&#039;ll have to implement them by yourself. Jump to [[Module_CustomFields_Dev#Hooks]].&lt;br /&gt;
&lt;br /&gt;
* If there is a list of &#039;&#039;&#039;contexts&#039;&#039;&#039; but the module cannot be found in the list: the hookmanager is defined but the module has not defined a context, you still have to implement the hooks. Jump to [[Module_CustomFields_Dev#Hooks]].&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;conf hooks_modules&#039;&#039;&#039;: this variable is not necessary, empty or not it doesn&#039;t matter. But if you see customfields and/or customfieldspdftest inside the list, it is a confirmation that CustomFields indeed is hooking the module correctly.&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;hooks attached&#039;&#039;&#039;, or CustomFields cannot be found in the list: CustomFields has not been properly configured to hook to this module, or you forgot to disable then re enable CustomFields in order to update the CustomFields&#039;s hooks. Jump to [[Module_CustomFields_Dev#Configuring_CustomFields_to_support_the_module]].&lt;br /&gt;
&lt;br /&gt;
* If there is no &#039;&#039;&#039;$user-&amp;gt;rights&#039;&#039;&#039;: there is either no standard security implemented in this module, or either the variable is called with another name. If there is no security, you don&#039;t need to configure it in CustomFields, but if there is a security, you need to find how the variable is called (like $object can get another name sometimes).&lt;br /&gt;
&lt;br /&gt;
* If there is &#039;&#039;&#039;$user-&amp;gt;rights&#039;&#039;&#039; but the module can&#039;t be found in the list: either the name for the rights is different (this often happens, the rights array is not yet standardized), or either the rights are hardcoded into the module&#039;s code (rights should be announced to the $user-&amp;gt;rights array, but sometimes it happens that the dev forgot to do so, and thus the right is entirely hardcoded and you can&#039;t find it in the dynamic rights array). In any of these two cases, you will have to manually read the module&#039;s code to find the right. Also, maybe there is no security implemented, but since $user-&amp;gt;rights is declared this is less probable.&lt;br /&gt;
&lt;br /&gt;
Here is an example of output on a correctly hooked module (invoice):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
table_element: facture - table_element_line: facturedet - contexts: Array&lt;br /&gt;
(&lt;br /&gt;
    [0] =&amp;gt; searchform&lt;br /&gt;
    [1] =&amp;gt; leftblock&lt;br /&gt;
    [2] =&amp;gt; toprightmenu&lt;br /&gt;
    [3] =&amp;gt; invoicecard&lt;br /&gt;
)&lt;br /&gt;
 - conf hooks_modules: Array&lt;br /&gt;
(&lt;br /&gt;
    [customfields] =&amp;gt; invoicecard:propalcard:productcard:ordercard:thirdpartycard:contactcard:ordersuppliercard:invoicesuppliercard:membercard:actioncard:projectcard:projecttaskcard:contractcard:interventioncard:doncard:tripsandexpensescard:taxvatcard:expeditioncard:invoicecard:propalcard:ordercard:ordersuppliercard:invoicesuppliercard&lt;br /&gt;
    [customfieldspdftest] =&amp;gt; Array&lt;br /&gt;
        (&lt;br /&gt;
            [0] =&amp;gt; pdfgeneration&lt;br /&gt;
            [1] =&amp;gt; ordersuppliercard&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
)&lt;br /&gt;
 - hooks attached: Array&lt;br /&gt;
(&lt;br /&gt;
    [invoicecard] =&amp;gt; Array&lt;br /&gt;
        (&lt;br /&gt;
            [customfields] =&amp;gt; ActionsCustomFields Object&lt;br /&gt;
                (&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
)&lt;br /&gt;
 - user rights: stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [user] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [user] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [lire] =&amp;gt; 1&lt;br /&gt;
                    [creer] =&amp;gt; 1&lt;br /&gt;
                    [password] =&amp;gt; 1&lt;br /&gt;
                    [supprimer] =&amp;gt; 1&lt;br /&gt;
                    [export] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [self] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [creer] =&amp;gt; 1&lt;br /&gt;
                    [password] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [user_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [readperms] =&amp;gt; 1&lt;br /&gt;
                    [write] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [self_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [readperms] =&amp;gt; 1&lt;br /&gt;
                    [writeperms] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [group_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [read] =&amp;gt; 1&lt;br /&gt;
                    [readperms] =&amp;gt; 1&lt;br /&gt;
                    [write] =&amp;gt; 1&lt;br /&gt;
                    [delete] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    [facture] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [lire] =&amp;gt; 1&lt;br /&gt;
            [creer] =&amp;gt; 1&lt;br /&gt;
            [invoice_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [unvalidate] =&amp;gt; 1&lt;br /&gt;
                    [send] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [valider] =&amp;gt; 1&lt;br /&gt;
            [paiement] =&amp;gt; 1&lt;br /&gt;
            [supprimer] =&amp;gt; 1&lt;br /&gt;
            [facture] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [export] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    [propale] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [lire] =&amp;gt; 1&lt;br /&gt;
            [creer] =&amp;gt; 1&lt;br /&gt;
            [valider] =&amp;gt; 1&lt;br /&gt;
            [propal_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [send] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [cloturer] =&amp;gt; 1&lt;br /&gt;
            [supprimer] =&amp;gt; 1&lt;br /&gt;
            [export] =&amp;gt; 1&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    [produit] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [lire] =&amp;gt; 1&lt;br /&gt;
            [creer] =&amp;gt; 1&lt;br /&gt;
            [supprimer] =&amp;gt; 1&lt;br /&gt;
            [export] =&amp;gt; 1&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    etc... a lot of other rights...&lt;br /&gt;
&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When you are finished with implementing the support of CustomFields in the module, you should delete these debug lines.&lt;br /&gt;
&lt;br /&gt;
== Implementing the support of CustomFields in the module ==&lt;br /&gt;
&lt;br /&gt;
If there is a module you want to implement the support of CustomFields, but does not yet has the required components, here is a guide on how to implement these components.&lt;br /&gt;
&lt;br /&gt;
=== Hooks ===&lt;br /&gt;
&lt;br /&gt;
Implementing hooks will allow custom fields to appear on your module&#039;s page. They will also be editable and they will save the data. For creation, you need to use a trigger (see the subchapter below).&lt;br /&gt;
&lt;br /&gt;
First you should be familiar with the way that hooks are implemented in modules. If you don&#039;t, please first [[Hooks_system#Implementation|read the hooks documentation]].&lt;br /&gt;
&lt;br /&gt;
An example case of implementation of hooks into a Dolibarr core file can be found in the [[Module_CustomFields_Cases|CustomFields Example Cases wiki page]].&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Datasheet hook&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
To implement CustomFields in your module, you must initialize the hook manager, then use the following hook context:&lt;br /&gt;
* formObjectOptions&lt;br /&gt;
&lt;br /&gt;
This hook must be placed everywhere in your code where you want custom fields to appear (eg: on the creation page and datasheet view page).&lt;br /&gt;
&lt;br /&gt;
Note: Generally, you will have to place the hook just before the end of a table tag (&#039;&#039;&#039;&amp;lt;/table&amp;gt;&#039;&#039;&#039;), so if you don&#039;t know where to place it (eg: if you&#039;re implementing CustomFields on a Dolibarr core module), just search for this tag in the page shown in the url (eg: /htdocs/commande/fiche.php).&lt;br /&gt;
&lt;br /&gt;
Eg: First load the hookmanager at the beginning of your php script:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Initialize technical object to manage hooks of thirdparties. Note that conf-&amp;gt;hooks_modules contains array array&lt;br /&gt;
include_once(DOL_DOCUMENT_ROOT.&#039;/core/class/hookmanager.class.php&#039;);&lt;br /&gt;
$hookmanager=new HookManager($db);&lt;br /&gt;
$hookmanager-&amp;gt;initHooks(array(&#039;yourmodule&#039;));&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then place the hook as many times as required:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// ... a lot of php code ...&lt;br /&gt;
&lt;br /&gt;
print &#039;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&#039;; // end of the last row of a table&lt;br /&gt;
&lt;br /&gt;
// Other attributes&lt;br /&gt;
$parameters=array(&#039;colspan&#039; =&amp;gt; &#039; colspan=&amp;quot;3&amp;quot;&#039;); // specifying the colspan is optional but will help CustomFields know how to correctly print the table&lt;br /&gt;
$reshook=$hookmanager-&amp;gt;executeHooks(&#039;formObjectOptions&#039;,$parameters,$object,$action);    // Note that $action and $object may have been modified by hook&lt;br /&gt;
&lt;br /&gt;
print &#039;&amp;lt;/table&amp;gt;&#039;; // end of the table, this is what you&#039;re looking for, place the hook just above&lt;br /&gt;
&lt;br /&gt;
// ... a lot of php code ... redo the same if you find another &amp;lt;/table&amp;gt; and there&#039;s a page where there&#039;s no custom fields but you want to place custom fields on there&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note2: If after implementing the hook you receive an error or custom fields don&#039;t appear, you may have to change $object to another variable, depending on what is called the main object (eg: in Agenda it&#039;s called $action and $actioncomm).&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Lines hooks&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
For product lines, you should anyway use the product lines template files: htdocs/core/tpl/freeproductline_edit.tpl.php&lt;br /&gt;
&lt;br /&gt;
If you don&#039;t, you can still manually implement the required hooks, what is required is that you supply $line (which is the current line object being processed) in the $parameters array:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$parameters=array(&#039;line&#039;=&amp;gt;$line,&#039;fk_parent_line&#039;=&amp;gt;$line-&amp;gt;fk_parent_line);&lt;br /&gt;
&lt;br /&gt;
echo $hookmanager-&amp;gt;executeHooks(&#039;formEditProductOptions&#039;,$parameters,$this,$action);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hooks names:&lt;br /&gt;
* formEditProductOptions&lt;br /&gt;
* formCreateProductOptions ($line is not required here, because we create a new product line! you can leave $parameters=array(); empty).&lt;br /&gt;
Note: there&#039;s no hook for viewing, because showing the customfields will clutter the visual field, but if you really want you can also do it by adding a formViewProductOptions hook.&lt;br /&gt;
&lt;br /&gt;
=== Triggers ===&lt;br /&gt;
&lt;br /&gt;
First you need to know how to implement triggers in Dolibarr. For more informations about how to implement triggers in your module, please read the [[Triggers#Manage_and_create_a_new_trigger.27s_action|triggers wiki page]].&lt;br /&gt;
&lt;br /&gt;
An example case of implementation of hooks into a Dolibarr core file can be found in the [[Module_CustomFields_Cases|CustomFields Example Cases wiki page]].&lt;br /&gt;
&lt;br /&gt;
Implementing triggers on your module will allow CustomFields to save the fields at creation (datasheet edition is already managed by hooks).&lt;br /&gt;
&lt;br /&gt;
You need to implement 3 triggers:&lt;br /&gt;
- on creation of object - eg: YOURMODULE_CREATE&lt;br /&gt;
- on creation of lines - eg: YOURMODULELINE_INSERT (optional, only if your module has product lines)&lt;br /&gt;
- on update of lines - eg: YOURMODULELINE_UPDATE (optional, only if your module has product lines)&lt;br /&gt;
&lt;br /&gt;
Note: you need to supply the sql id of the newly created object before calling the trigger.&lt;br /&gt;
&lt;br /&gt;
Eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$this-&amp;gt;id = $this-&amp;gt;db-&amp;gt;last_insert_id(MAIN_DB_PREFIX.&amp;quot;yourmodule_table_element&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
// Appel des triggers&lt;br /&gt;
include_once(DOL_DOCUMENT_ROOT . &amp;quot;/core/class/interfaces.class.php&amp;quot;);&lt;br /&gt;
$interface=new Interfaces($this-&amp;gt;db);&lt;br /&gt;
$result=$interface-&amp;gt;run_triggers(&#039;YOURMODULE_CREATE&#039;,$this,$user,$langs,$conf);&lt;br /&gt;
if ($result &amp;lt; 0) {&lt;br /&gt;
    $error++; $this-&amp;gt;errors=$interface-&amp;gt;errors;&lt;br /&gt;
}&lt;br /&gt;
// Fin appel triggers&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note2: if it&#039;s a Dolibarr core module, you can easily find the right place to put the trigger by searching for &#039;create sql&#039; in the class subfolder (eg: URL is /htdocs/commande/fiche.php, the class is in /htdocs/commande/class/commande.class.php).&lt;br /&gt;
&lt;br /&gt;
Note3: CustomFields also supports a few other triggers like _CLONE, _UPDATE, _MODIFY, but they are not required and may not work depending on how your module works to manage these actions.&lt;br /&gt;
&lt;br /&gt;
== Configuring CustomFields to support the module ==&lt;br /&gt;
&lt;br /&gt;
=== Preliminary work ===&lt;br /&gt;
&lt;br /&gt;
You need to get the parameters of this module, and you will then put them in the CustomFields configuration file (conf_customfields.lib.php).&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Getting the parameters automatically&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
You can use the [[Module_CustomFields_Dev#Preliminary_test|preliminary test]] to easily get all those parameters.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Getting the parameters manually&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
To configure CustomFields to support a module, you need at least 5 parameters:&lt;br /&gt;
* the module&#039;s table_name (name of the sql table where the module stores its datas)&lt;br /&gt;
* the module&#039;s context (name given by the module to the hookmanager)&lt;br /&gt;
* the module&#039;s idvar (name of the variable that holds the sql row id of the record)&lt;br /&gt;
* the module&#039;s trigger action(s) (triggers on creation and update, see the related chapter above)&lt;br /&gt;
* the module&#039;s rights (for security purposes)&lt;br /&gt;
&lt;br /&gt;
To get these parameters manually, you can take a look inside the code of the module&#039;s files you want to implement:&lt;br /&gt;
* module&#039;s table name: either find it by yourself in the database (looks like llx_themodulename) or by printing it in actions_customfields.class.php by adding &#039;&#039;&#039;print_r($parameters)&#039;&#039;&#039; and search for $parameters-&amp;gt;table_element&lt;br /&gt;
* module&#039;s context: search for &amp;quot;callHooks(&amp;quot; without the quotes or take a look at the wiki: http://wiki.dolibarr.org/index.php/Hooks_system&lt;br /&gt;
or you can get it by printing it in the /htdocs/customfields/class/actions_customfields.class.php by adding print_r($parameters) and search for $parameters-&amp;gt;context&lt;br /&gt;
* module&#039;s trigger actions: search for &amp;quot;run_triggers(&amp;quot; or take a look at the wiki: http://wiki.dolibarr.org/index.php/Triggers#List_of_known_triggers_actions&lt;br /&gt;
* module&#039;s idvar: look at the url when you access one of the module&#039;s record, and you should see something like fiche.php?xxxid=12 - you need the xxxid (eg: socid, facid, etc.).&lt;br /&gt;
* module&#039;s security: in the page shown in URL when accessing the module&#039;s datasheet, edit this file and add &#039;&#039;&#039;print($user-&amp;gt;rights)&#039;&#039;&#039;, then in the list try to find the correct right (generally something like $user-&amp;gt;modulename-&amp;gt;creer). You can also try to search for $user-&amp;gt;rights in the sourcecode of this module.&lt;br /&gt;
&lt;br /&gt;
With these values, edit the config file (/htdocs/customfields/conf/conf_customfields.lib.php), particularly the $modulesarray and $triggersarray variables:&lt;br /&gt;
&lt;br /&gt;
=== Configure hooks and main parameters ===&lt;br /&gt;
&lt;br /&gt;
Add every parameters except triggers in the $modulesarray:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$modulesarray = array(&lt;br /&gt;
                      array(&#039;context&#039;=&amp;gt;&#039;invoicecard&#039;, &#039;table_element&#039;=&amp;gt;&#039;facture&#039;, &#039;idvar&#039;=&amp;gt;&#039;facid&#039;, &#039;rights&#039;=&amp;gt;array(&#039;$user-&amp;gt;rights-&amp;gt;facture-&amp;gt;creer&#039;)), // Client Invoice&lt;br /&gt;
                      array(&#039;context&#039;=&amp;gt;&#039;membercard&#039;, &#039;table_element&#039;=&amp;gt;&#039;adherent&#039;, &#039;idvar&#039;=&amp;gt;&#039;rowid&#039;, &#039;rights&#039;=&amp;gt;array(&#039;$user-&amp;gt;rights-&amp;gt;adherent-&amp;gt;creer&#039;), &#039;tabs&#039;=&amp;gt;array(&#039;objecttype&#039;=&amp;gt;&#039;member_admin&#039;, &#039;function&#039;=&amp;gt;&#039;member_admin_prepare_head&#039;, &#039;lib&#039;=&amp;gt;DOL_DOCUMENT_ROOT.&#039;/core/lib/member.lib.php&#039;)), // Members / Adherents&lt;br /&gt;
                     ); // Edit me to add the support of another module - NOTE: Lowercase only!&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are also a lot of others parameters you can give to CustomFields so that the module gets better managed or to add a few other functionnalities, here is a non-exhaustive list:&lt;br /&gt;
* context: name of the module&#039;s context.&lt;br /&gt;
* table_element: name of the sql table that holds the object&#039;s datas.&lt;br /&gt;
* idvar: name of the variable that holds the id of the object in the sql database (by default &#039;id&#039; or &#039;rowid&#039;).&lt;br /&gt;
* rights: prevent the custom fields to be editable unless all the rights set here are enabled (preventing custom fields to be viewed is useless because they won&#039;t show anyway if the page can&#039;t be loaded due to insufficient rights, so this is directly handled by the module).&lt;br /&gt;
* tabs=&amp;gt;array(objecttype, function, lib, tabname, tabtitle): used to embed CustomFields&#039;s admin panel into another module&#039;s admin page.&lt;br /&gt;
&lt;br /&gt;
=== Configure creation triggers ===&lt;br /&gt;
&lt;br /&gt;
Add the triggers in $triggersarray:&lt;br /&gt;
$triggersarray = array(&amp;quot;order_create&amp;quot;=&amp;gt;&amp;quot;commande&amp;quot;,&lt;br /&gt;
                       &amp;quot;yourmodule_triggeraction1&amp;quot;=&amp;gt;&amp;quot;yourmodule_tablename&amp;quot;,&lt;br /&gt;
                       &amp;quot;yourmodule_triggeraction2&amp;quot;=&amp;gt;&amp;quot;yourmodule_tablename&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
Note: generally you will be looking to implement the _CREATE.&lt;br /&gt;
&lt;br /&gt;
Note2: if a triggeraction is not recognized by CustomFields (eg: mymodule_addpayment) then by default CustomFields will assimilate it to a _create action (it will create and save the values for the custom fields, just for a module&#039;s creation page).&lt;br /&gt;
&lt;br /&gt;
Note3: CustomFields also supports a few other triggers like _CLONE, _UPDATE, _MODIFY, but they are not required and may not work depending on how your module works to manage these actions.&lt;br /&gt;
&lt;br /&gt;
=== Configuration troubleshooting ===&lt;br /&gt;
&lt;br /&gt;
* If you cannot see the custom fields appear on your module&#039;s page, this means the hooks are not implemented or configured in CustomFields, please read the related chapters.&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet but there&#039;s no edit button icon: the module implements security but you did not configure it properly in CustomFields, please find the right security in $user-&amp;gt;rights and set it in conf_customfields.lib.php.&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the creation page but are not saved: you forgot to set the trigger in $triggersarray, or the creation trigger does not exist for this module and you have to implement it. Jump to [[Module_CustomFields_Dev#Triggers]].&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet but not on the creation page (or vice versa): A hook is missing on the page where the custom fields do not appear. Jump to [[Module_CustomFields_Dev#Hooks]].&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet with the edit button, but when the edit button is clicked, everything disappear: either the $idvar is not properly set (look at the URL to know if you need to set idvar), or the hook is not properly made ($object is not defined and it&#039;s called by another name, but the hook use $object instead of the correct variable).&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet with the edit button, can be edited but cannot be saved (the data is not saved): same as the previous point: either $idvar or $object bug.&lt;br /&gt;
&lt;br /&gt;
Also you might find useful to &#039;&#039;&#039;[[Module_CustomFields#General_advices_for_troubleshooting|enable php errors]]&#039;&#039;&#039; to better track errors if that&#039;s not already done.&lt;br /&gt;
&lt;br /&gt;
= Example cases =&lt;br /&gt;
&lt;br /&gt;
You can find a compilation of concrete, practical examples on the [[Module_CustomFields_Cases]] wiki page.&lt;br /&gt;
&lt;br /&gt;
= TO DO =&lt;br /&gt;
&lt;br /&gt;
You will find below the public list of features that should be implemented in the future.&lt;br /&gt;
&lt;br /&gt;
If you have an idea about a new feature that isn&#039;t listed below, feel free to add it to the list!&lt;br /&gt;
&lt;br /&gt;
== To Do ==&lt;br /&gt;
Nothing here!&lt;br /&gt;
&lt;br /&gt;
== To Document ==&lt;br /&gt;
* Automatic recopy on cloning for custom fields and products&#039; lines custom fields.&lt;br /&gt;
* new API function: customfields_clone_or_recopy($object, $fromobject, $action2 = null)&lt;br /&gt;
&lt;br /&gt;
== Should do ==&lt;br /&gt;
* Add the possibility to mark a custom field to be shown in tables listings (eg: as a column in products listings) with &amp;quot;order by this column&amp;quot; buttons and stuffs. Will be difficult if the hooks are not yet implemented in listings.&lt;br /&gt;
* Checkbox (multiple checkboxes, one for each possible value) and multiselect (big select and can use SHIFT to multiselect) (store in XML file) (but impossible with constrained fields, can&#039;t store multiple foreign keys ids in one single sql field, but would work for other types like enum)&lt;br /&gt;
use fieldset, input checkbox and label for, eg:&lt;br /&gt;
    &amp;lt;fieldset&amp;gt;&amp;lt;legend&amp;gt;&amp;lt;label for=&amp;quot;cf_productslist&amp;quot;&amp;gt; $langs-&amp;gt;trans(&#039;cf_productslist&#039;)&amp;lt;/label&amp;gt;&amp;lt;/legend&amp;gt;&lt;br /&gt;
    &amp;lt;input type=&amp;quot;checkbox&amp;quot; name=&amp;quot;cf_productslist_choice1&amp;quot; value=&amp;quot;1&amp;quot; id=&amp;quot;cf_productslist_choice1&amp;quot; /&amp;gt;&amp;lt;label for=&amp;quot;cf_productslist_choice1&amp;quot;&amp;gt; $langs-&amp;gt;trans(&#039;cf_productslist_choice1&#039;)&amp;lt;/label&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
    &amp;lt;input type=&amp;quot;checkbox&amp;quot; name=&amp;quot;cf_productslist_choice2&amp;quot; value=&amp;quot;1&amp;quot; id=&amp;quot;cf_productslist_choice2&amp;quot; /&amp;gt;&amp;lt;label for=&amp;quot;cf_productslist_choice2&amp;quot;&amp;gt; $langs-&amp;gt;trans(&#039;cf_productslist_choice2&#039;)&amp;lt;/label&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
    etc...&lt;br /&gt;
    &amp;lt;/fieldset&amp;gt;&lt;br /&gt;
* Add a javascript options values generator for the enum type (a hidden input that would be shown only when DropdownBox type is selected and that would permit to add options by clicking a plus button).&lt;br /&gt;
* Replace triggers in customfields class by hookmanager?&lt;br /&gt;
* AJAX previsualisation of the HTML custom field that will be created.&lt;br /&gt;
* In ODTs, create special tags to directly access day, month or year value of a date type field independently of the rest.&lt;br /&gt;
* New overloading function&#039;s action: &amp;quot;get&amp;quot;, similar to view except that it works not only on Dolibarr&#039;s view but whenever someone tries to access this field&#039;s value.&lt;br /&gt;
* Implementation of custom fields in CSV exports (may need a few new hooks in Dolibarr?).&lt;br /&gt;
* Add Upload and Image field types (this would allow for an easier integration inside ODT and PDF templates than the current workflow of Dolibarr).&lt;br /&gt;
&lt;br /&gt;
== Known bugs ==&lt;br /&gt;
* in product and service modules, if you edit a field, the proposals and other fields below won&#039;t be shown, you need to refresh the page. This problem resides in Dolibarr I think (since we are simply using a hook).&lt;br /&gt;
* ficheinter (Intervention module) custom fields will stay in create mode just after create page is submitted (this is because of the $action that is not standard - $action=edit or something).&lt;br /&gt;
&lt;br /&gt;
== Never/Maybe one day ==&lt;br /&gt;
* Add support for repeatable (predefined) invoices (the way it is currently managed makes it very difficult to manage this without making a big exception, adding specific functions in customfields modules that would not at all will be reusable anywhere else, when customfields has been designed to be as generic as possible to support any module and any version of dolibarr, because it&#039;s managed by a totally different table while it&#039;s still managed by the same module, CustomFields work with the paradigm: one module, one table).&lt;br /&gt;
* Add an AJAX select box for constrained values : when a constrained type is selected and a table is selected, a hidden select box would show up with the list of the fields of this table to choose the values that will be printed as the values for this customfield (eg: for table llx_users you could select the &amp;quot;nom&amp;quot; field and then it would automatically prepend &amp;quot;nom_&amp;quot; to the field&#039;s name).&lt;br /&gt;
* Fine-grained rights management: rights per group, per user, and per field.&lt;br /&gt;
* Replace overloading functions (extend class) by hookmanager if possible (but how to simulate the *full switch? Plus this will force users to make their own modules to make those functions, and to disable/renable their modules everytime they will add a new function...).&lt;br /&gt;
* Refactor the trigger array: merge it with the modulesarray (along with a new way to specify which customfields trigger action a trigger should correspond, eg: &#039;linebill_insert&#039;=&amp;gt;&#039;customfields_create&#039;). But the triggers aren&#039;t necessarily associated with a specific module...&lt;/div&gt;</summary>
		<author><name>Lrq3000</name></author>
	</entry>
	<entry>
		<id>https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Dev&amp;diff=33120</id>
		<title>Module CustomFields Dev</title>
		<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Dev&amp;diff=33120"/>
		<updated>2015-01-22T21:43:17Z</updated>

		<summary type="html">&lt;p&gt;Lrq3000: /* Recursive fetching referenced records of a constrained field */ Adding a note&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:CustomFields]]&lt;br /&gt;
{{TemplateDocDevEn}}&lt;br /&gt;
{{TemplateModEN}}&lt;br /&gt;
{{BasculeDevUserEn|&lt;br /&gt;
name=CustomFields|&lt;br /&gt;
num=8500|&lt;br /&gt;
devdoc=This page|&lt;br /&gt;
userdoc=[[Module_CustomFields]]|}}&lt;br /&gt;
[[Category:FAQ EN]]&lt;br /&gt;
&lt;br /&gt;
These notes are aimed to developpers who want to extend the functionnalities of the module or want to use it for extreme cases.&lt;br /&gt;
This is not meant to guide implementers on how to implement and configure the module (see the related chapter for that).&lt;br /&gt;
&lt;br /&gt;
If you are an implementer and just want to use CustomFields with its normal functions, you can read the [[Module_CustomFields|user documentation of CustomFields]].&lt;br /&gt;
&lt;br /&gt;
You can also jump directly to [[Module_CustomFields_Cases]] wiki page for a collection of practical examples.&lt;br /&gt;
&lt;br /&gt;
= Principles =&lt;br /&gt;
&lt;br /&gt;
CustomFields is simply a wrapper for SQL databases.&lt;br /&gt;
&lt;br /&gt;
The module is based on SQL to manage the fields and most of the stuff. It uses and understands standard SQL.&lt;br /&gt;
&lt;br /&gt;
The main principles of CustomFields are:&lt;br /&gt;
&lt;br /&gt;
* one SQL column = one Dolibarr field.&lt;br /&gt;
* one SQL type = one method to show and edit the field in Dolibarr.&lt;br /&gt;
* almost everything should be configurable from the configuration file, or admin interface.&lt;br /&gt;
* be independent from Dolibarr&#039;s data structures.&lt;br /&gt;
&lt;br /&gt;
CustomFields sits besides Dolibarr, and does not mix with it, even if in the Dolibarr&#039;s interface it looks like CustomFields is embedded inside Dolibarr, in reality, CustomFields is totally independent.&lt;br /&gt;
&lt;br /&gt;
= Goals =&lt;br /&gt;
&lt;br /&gt;
This module was made to provide an easy way to manage custom sql fields, not only for users and implementers, but also for developpers to use in their own module:&lt;br /&gt;
* by easily adding the support of custom fields for your own module by using this module;&lt;br /&gt;
* by reusing custom fields from another module in your own module;&lt;br /&gt;
* by managing sql fields in your own module by using the CustomFields class (which will take care of everything from printing, editing to PDF/ODT templating).&lt;br /&gt;
&lt;br /&gt;
So this module is not only an auxiliary of other modules, but it can also be the core part of your own module by using it as your SQL query class for fields.&lt;br /&gt;
&lt;br /&gt;
= SQL structure =&lt;br /&gt;
&lt;br /&gt;
A CustomFields table needs to respect three parameters that never change:&lt;br /&gt;
&lt;br /&gt;
* table&#039;s name (format: llx_*dolibarrmodule*_customfields where *dolibarrmodule* = table_element in conf_customfields.lib.php)&lt;br /&gt;
* first column: rowid, which is the primary key for each field&lt;br /&gt;
* second column: fk_*dolibarrmodule* which is the foreign key that links every customfields record to the Dolibarr&#039;s module that is associated (invoice, order, etc...).&lt;br /&gt;
&lt;br /&gt;
Apart from the table&#039;s name and the first two special columns, every other columns are just standard sql columns that you can freely create, edit or delete as you want, from the module&#039;s administration page or from a third-party SQL management tool (such as phpMyAdmin).&lt;br /&gt;
&lt;br /&gt;
You can also create more complex sql fields: constraints, foreign keys, transformations, other sql types, etc...).&lt;br /&gt;
&lt;br /&gt;
Then the module will try to manage the sql fields you manually created the best it can:&lt;br /&gt;
* by printing appropriate input fields in the Dolibarr gui interface.&lt;br /&gt;
* by providing the developper an easy access to these fields in php (eg: $customfields-&amp;gt;cf_myfield or $object-&amp;gt;customfields-&amp;gt;cf_myfield) for any use, including PDF templates and ODT templates, or in any php script (so that you can use these fields in your own module).&lt;br /&gt;
&lt;br /&gt;
= SQL Indexes =&lt;br /&gt;
&lt;br /&gt;
The module automatically adds an index on the primary key and foreign key (first and second columns).&lt;br /&gt;
&lt;br /&gt;
To further optimize the performances of your database, and particularly the customfields tables, you may want to manually add indexes based on usage.&lt;br /&gt;
&lt;br /&gt;
Also, you may add indexes on remote tables used by constrained fields, which could optimize even more the performances since constrained fields have the worst performance drawback for CustomFields.&lt;br /&gt;
&lt;br /&gt;
= SQL Compatibility mode =&lt;br /&gt;
&lt;br /&gt;
The module relies on referential integrity checks and foreign keys of your DBMS to keep the database clean and to provide the constrained fields feature. Referential integrity checks is part of the SQL standards and should be used whenever possible, which always was a main goal for CustomFields (to be respectful of SQL standards).&lt;br /&gt;
&lt;br /&gt;
Unluckily, a few DBMS systems still don&#039;t support this critical functionality of the SQL standard, for example MySQL enables MyISAM engine by default, which doesn&#039;t support this feature.&lt;br /&gt;
&lt;br /&gt;
In these cases, you can either do two things:&lt;br /&gt;
* Switch to an engine which support referential integrity. For example with MySQL, you can enable InnoDB, which support referential integrity checks and foreign keys.&lt;br /&gt;
* Use CustomFields compatibility mode (more infos below).&lt;br /&gt;
&lt;br /&gt;
In the case your database does not support referential integrity checks, CustomFields will automatically switch to the SQL compatibility mode, which will allow you to use CustomFields even on databases where foreign keys are not possible.&lt;br /&gt;
&lt;br /&gt;
In compatibility mode, you will get nearly all CustomFields advanced features such as Constrained fields, but the customfields&#039; tables in the database won&#039;t be cleaned automatically (no delete on cascade, meaning that when you delete an object, the linked custom fields will still be kept in your database), and constrained fields won&#039;t be updated automatically on update.&lt;br /&gt;
&lt;br /&gt;
Nevertheless, these missing cleanup functionalities won&#039;t be seen by your users because they will never be accessible: on deletion of an object, the custom fields won&#039;t ever be accessible again from Dolibarr&#039;s interface, and object&#039;s ids are never changed since Dolibarr also relies on this. Thus in compatibility mode, CustomFields will act just like normally, but your database will be a bit less clean.&lt;br /&gt;
&lt;br /&gt;
= JSON extension for extra options =&lt;br /&gt;
&lt;br /&gt;
== Introduction to JSON in CustomFields ==&lt;br /&gt;
The relational model is very great to store and fetch data with a declarative language, and this is very powerful, but this model also imposes a few limitations, for example the relations must be well formatted and predefined.&lt;br /&gt;
&lt;br /&gt;
That&#039;s why we finally implemented a &#039;&#039;&#039;JSON extension&#039;&#039;&#039; to manage &#039;&#039;&#039;extra options&#039;&#039;&#039; and associate them with custom fields. This allows to add many properties (or columns if you are more used to the relational model) on-the-fly without having to predefine them prior to storing and using them.&lt;br /&gt;
&lt;br /&gt;
This allows to add an undefined number of columns/properties/extra options that will help manage special behaviours for custom fields. For example, it is currently used to allow to change the order of appearance of custom fields, and to store and manage remote table and columns datas for constrained fields on DBMS that do not support Foreign Keys checks.&lt;br /&gt;
&lt;br /&gt;
We chose JSON over XML because the library are simpler and the data footprint smaller, and because CustomFields does not need advanced features of XML like XQuery, data typing and meta structures (at least not for now).&lt;br /&gt;
&lt;br /&gt;
The other advantage is that php objects can directly be encoded into JSON without any preprocessing, and can then as straightly be decoded from JSON back into a php object.&lt;br /&gt;
&lt;br /&gt;
A simple mean to encode and decode back an object into and from a storage place, as well as allow users to easily add/remove extra options were the main requirement, and JSON (+ JSON libraries for PHP) meet this criteria.&lt;br /&gt;
&lt;br /&gt;
However, note that the extra options are now encoded as an associative array, so as to avoid any ambiguities (there&#039;s no associative array in JSON, they are converted to objects).&lt;br /&gt;
&lt;br /&gt;
== Implementation of JSON ==&lt;br /&gt;
The implementation is very simple: a table called &#039;&#039;&#039;llx_customfields_extraoptions&#039;&#039;&#039; is created when you enable the CustomFields module, and this table only contains three columns:&lt;br /&gt;
* table_name (varchar)&lt;br /&gt;
* column_name (varchar)&lt;br /&gt;
* extraoptions (blob)&lt;br /&gt;
&lt;br /&gt;
(table_name, column_name) both forms the primary key of the table, and links to columns (custom fields) of other customfields tables.&lt;br /&gt;
extraoptions is a blob, which allows to store the data as binary (as-is) without encoding (which solves a big problem of JSON which is encoding special and foreign characters, here there is no problem at all).&lt;br /&gt;
&lt;br /&gt;
When fetching custom fields, the module simply JOIN the extraoptions table, which adds an &#039;&#039;&#039;extraoptions&#039;&#039;&#039; column for each custom field in the SQL result. Then it is quite easy to just json_decode() the data returned inside the extraoptions column.&lt;br /&gt;
&lt;br /&gt;
This methodology allows to have all the advantages of a relational database, with the flexibility of a free-schema model:&lt;br /&gt;
* everything is stored inside the database (only one storage place)&lt;br /&gt;
* fixed columns allows for relational joining and optimization&lt;br /&gt;
* thus queries (fetching) are optimised&lt;br /&gt;
* at the same time, the extraoptions are automatically retrieved and associated with every custom field, and whenever needed they can be used&lt;br /&gt;
* and thus we also get a flexible model to &#039;&#039;&#039;add extra (virtual) columns whenever needed&#039;&#039;&#039; without having to alter the relational database schema&lt;br /&gt;
&lt;br /&gt;
Thus no critical information is and should be stored inside the extraoptions column because they would not be query-able, only extra options that are just used to tweak the management of those fields inside the PHP application should be stored. But this is a wonderful way to add non critical informations that still allows to develop more complex features and management for special custom fields.&lt;br /&gt;
&lt;br /&gt;
== CustomFields JSON API ==&lt;br /&gt;
&lt;br /&gt;
=== Storing extra options ===&lt;br /&gt;
&lt;br /&gt;
You can use the &#039;&#039;&#039;setExtra()&#039;&#039;&#039; method to set extra options of a custom field.&lt;br /&gt;
&lt;br /&gt;
Eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$extra = array(); // $extra should always be an associative array, it&#039;s no longer an object&lt;br /&gt;
$extra[&#039;myproperty&#039;] = &#039;Anything I want here&#039;; // set a property/extra option like this&lt;br /&gt;
$extra[&#039;category&#039;][&#039;subcategory&#039;][&#039;prop&#039;] = true; // we can set recursive properties, there&#039;s not any limit to how deep the hierarchy can be&lt;br /&gt;
&lt;br /&gt;
require_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomField($db, &#039;currentmodule&#039;);&lt;br /&gt;
$fieldid = &#039;mycustomfields&#039;; // $fieldid can either be a column_name or an ordinal_position from information_schema.columns&lt;br /&gt;
$result = $customfields-&amp;gt;setExtra($fieldid, $extra); // store the extra options inside the database&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can also use addCustomField() or updateCustomField() with an $extra array, and these functions will also call the setExtra() method.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: the extra options you set are &#039;&#039;&#039;appended&#039;&#039;&#039; to the ones already stored in the database. However, you can also overwrite previous options, just use the same name of property but with a different value (the replacement is recursive, you can do it at any level in the hierarchy).&lt;br /&gt;
&lt;br /&gt;
=== Fetching extra options ===&lt;br /&gt;
&lt;br /&gt;
Extra options are automatically fetched along with a custom field structure, decoded, and stored inside the extra sub-property of any field object, or CustomFields object.&lt;br /&gt;
&lt;br /&gt;
When fetched, extra options are stored as a sub-object for the current field object (which itself is stored as a subobject of the CustomFields object if caching is enabled).&lt;br /&gt;
&lt;br /&gt;
Eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
require_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomField($db, &#039;currentmodule&#039;);&lt;br /&gt;
$fields = fetchAllFieldsStruct(); // fetching fields structure will automatically fetch extra options at the same time. This works with fetchAllFieldsStruct() and fetchFieldStruct()&lt;br /&gt;
&lt;br /&gt;
// Now that you fetched the fields structure, you can just access the extra options sub-properties:&lt;br /&gt;
print_r($fields-&amp;gt;mycustomfield-&amp;gt;extra); // will print all extra options for the field mycustomfield&lt;br /&gt;
print_r($customfields-&amp;gt;fields-&amp;gt;mycustomfield-&amp;gt;extra); // you can also access the same field(s) directly from the CustomFields object if caching is enabled (default behaviour)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Modifying extra options ===&lt;br /&gt;
&lt;br /&gt;
You can just combine the two previous points to edit extra options of a custom field: just load a custom field, edit on-the-fly the extra property with PHP code, and then store it back directly into the database:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
require_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomField($db, &#039;currentmodule&#039;);&lt;br /&gt;
$fields = fetchAllFieldsStruct();&lt;br /&gt;
&lt;br /&gt;
$fields-&amp;gt;mycustomfield-&amp;gt;extra[&#039;newproperty&#039;] = &#039;This is a new property&#039;; // add a new property over the old extra options sub-object&lt;br /&gt;
$fields-&amp;gt;mycustomfield-&amp;gt;extra[&#039;mycategory&#039;] = &#039;Overwrite the old category&#039;; // this will replace the previous value of &#039;mycategory&#039; extra option&lt;br /&gt;
&lt;br /&gt;
$customfields-&amp;gt;setExtra(&#039;mycustomfield&#039;, $fields-&amp;gt;mycustomfield-&amp;gt;extra); // store the modified extra options back into the database for future usage&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= CustomFields API =&lt;br /&gt;
&lt;br /&gt;
== Introduction to CustomFields API ==&lt;br /&gt;
&lt;br /&gt;
This chapter will cover the basics on how to manually and concretely use the CustomFields class to create/update/fetch/manage custom fields.&lt;br /&gt;
&lt;br /&gt;
This will give you a better understanding of how CustomFields works, as well as giving you insights on how to use it for your own applications.&lt;br /&gt;
&lt;br /&gt;
Important note: contrary to the Facade API, the core CustomFields class use columns names without any prefix, so don&#039;t try to use the prefix, it won&#039;t work! For example: if your field is named &#039;mycustomfield&#039; and prefix is &#039;cf_&#039;, when you use the Facade API you should use &#039;$object-&amp;gt;$path-&amp;gt;cf_mycustomfield&#039; but when you use the core CustomFields class directly, $fieldname = &#039;mycustomfield&#039;.&lt;br /&gt;
&lt;br /&gt;
== Prerequisite to use the CustomFields class ==&lt;br /&gt;
&lt;br /&gt;
Before using the CustomFields class, you first must instanciate it. This is a necessary preliminary step before using any of the methods that will be covered inside this chapter, and thus will not be repeated, only shown once here.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Include the CustomFields class (which contains the main API)&lt;br /&gt;
require_once(dirname(__FILE__).&#039;/../class/customfields.class.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Instanciate a CustomFields object&lt;br /&gt;
global $db; // the database handler created by Dolibarr&lt;br /&gt;
$table_element = &#039;currentmodule&#039;; // where $table_element is the database&#039;s table name for the module, without the Dolibarr prefix (eg: for &#039;llx_facture&#039;, table_element is &#039;facture&#039;). You can also find it inside the $object properties (just print_r() it).&lt;br /&gt;
$customfields = new CustomFields($db, $table_element); // instanciate the CustomFields class inside $customfields object&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
CustomFields keeps track of errors that can happen when executing methods and SQL queries inside the &#039;&#039;&#039;error&#039;&#039;&#039; and &#039;&#039;&#039;errors&#039;&#039;&#039; properties, which you can print with the printErrors() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Do something with CustomFields before&lt;br /&gt;
&lt;br /&gt;
if (count($customfields-&amp;gt;errors) &amp;gt; 0) { // If there is at least one error&lt;br /&gt;
    $customfields-&amp;gt;printErrors(); // print errors&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// You can also print your own error message, in two manners:&lt;br /&gt;
&lt;br /&gt;
// 1- submit your own message&lt;br /&gt;
$customfields-&amp;gt;printErrors(&#039;Your own error message here&#039;); // this will print ONLY your error message, using the Dolibarr&#039;s AJAX error dialog (standard way of printing errors)&lt;br /&gt;
&lt;br /&gt;
// 2- append your message in the customfields errors&lt;br /&gt;
$customfields-&amp;gt;errors[] = &#039;Your own error message to be appended with other CustomFields messages&#039;;&lt;br /&gt;
$customfields-&amp;gt;printErrors(); // Here your error message will be appended, thus all error messages will be printed. This allows you for example to add more intelligible informations on a specific error.&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== General purpose SQL methods ==&lt;br /&gt;
&lt;br /&gt;
CustomFields provide an interface to easily execute various common SQL requests, as well as your own custom SQL requests.&lt;br /&gt;
&lt;br /&gt;
* fetchAllTables() fetch a list of all the tables in the database&lt;br /&gt;
* fetchPrimaryField($table) fetch the column_name (and only the column_name!) of the primary field of a table. You can then use fetchFieldStruct()  with the column_name to get any information you want.&lt;br /&gt;
* fetchAny($columns, $table, $where=&#039;&#039;, $orderby=&#039;&#039;, $limitby=&#039;&#039;) allows you to issue any simple SQL query command, for example fetchAny(&#039;*&#039;, &#039;sometable&#039;) is equivalent to SELECT * FROM sometable; (but then everything is managed automatically and result is returned to you)&lt;br /&gt;
* executeSQL($sql, $eventname) to execute any SQL query you want, this will return you a resource (and NOT a processed nuplet like fetchAny!). $eventname can be anything, it&#039;s just for Dolibarr logging facility.&lt;br /&gt;
* executeMultiSQL($sql, $eventname) to execute multiple SQL queries&lt;br /&gt;
* fetchFieldStruct($id=null, $nohide=false, $table=null, $cachepath=null) allows you to fetch the field&#039;s SQL structure of not only custom fields, but of any database&#039;s column using the $table variable. Eg: fetchFieldStruct(&#039;mycolumn&#039;, true, &#039;mytable&#039;);&lt;br /&gt;
* probeTable($table) helps you to check if a table exists in the database&lt;br /&gt;
* fetch_object($resource) will return a PHP object from a $resource, similarly to mysql_fetch_object() but with two advantages: it works on several DBMS (not only mysql), and it always return lowercase properties names (else depending on the configuration of the DBMS, the request can return uppercase or mixed case properties names).&lt;br /&gt;
* escape() and reverse_escape() are similare to mysql_real_escape but is reversible and DBMS-independent (so you can use it with other DBMSes).&lt;br /&gt;
&lt;br /&gt;
== Managing CustomFields structure ==&lt;br /&gt;
&lt;br /&gt;
The first step into CustomFields is to create and manage the structure of a custom field.&lt;br /&gt;
&lt;br /&gt;
=== Creating a custom field definition ===&lt;br /&gt;
&lt;br /&gt;
To create a custom field, you can use the addCustomField() method.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;addCustomField($fieldname, $type, $size, $nulloption, $defaultvalue = null, $constraint = null, $customtype = null, $customdef = null, $customsql = null, $fieldid = null, $extra = null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will take care of forging the (complicated) SQL query with the forgeSQLCustomField() method, and will return you an error if there was a problem.&lt;br /&gt;
&lt;br /&gt;
=== Updating a custom field definition ===&lt;br /&gt;
&lt;br /&gt;
To update a custom field, you can use the updateCustomField() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;&lt;br /&gt;
updateCustomField($fieldid, $fieldname, $type, $size, $nulloption, $defaultvalue, $constraint = null, $customtype = null, $customdef = null, $customsql = null, $extra = null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: the updateCustomField() and addCustomField() methods can both be used since updateCustomField() is in fact an alias for addCustomField(). CustomFields will automatically detect if a custom field already exists, and update it if that&#039;s true, or create it.&lt;br /&gt;
&lt;br /&gt;
Note2: updating a custom field will automatically take care of the constraints and foreign keys on its own, thus you can change the foreign key, or remove it altogether if set to null.&lt;br /&gt;
&lt;br /&gt;
=== Delete a custom field definition ===&lt;br /&gt;
&lt;br /&gt;
You can easily delete a custom field by column_name or by ordinal_position:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;deleteCustomField($id);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: constraints and foreign keys will also be deleted automatically so you shouldn&#039;t encounter any error.&lt;br /&gt;
&lt;br /&gt;
=== Fetch one custom field&#039;s SQL structure ===&lt;br /&gt;
&lt;br /&gt;
To get the SQL structure of a custom field, which can be quite useful to know for example its datatype or if it has a foreign key, or accepts null values, you can use the fetchFieldStruct() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
fields = $customfields-&amp;gt;fetchFieldStruct($id=null, $nohide=false, $table=null, $cachepath=null); // where $id can be a column_name, an ordinal_position from information_schema.columns or null if you want to fetch all the columns at once&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method will either return null, either a single field object, either an array on field objects.&lt;br /&gt;
&lt;br /&gt;
Note: this function will use caching by default to accelerate future results, but you can disable it, or even change the default cache path. For more informations please read the comments in the source code.&lt;br /&gt;
&lt;br /&gt;
=== Fetch several custom fields&#039; SQL structures ===&lt;br /&gt;
&lt;br /&gt;
Use the fetchAllFieldsStruct() method, which is an alias for fetchFieldStruct():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;fetchAllFieldsStruct($nohide=false, $table=null, $cachepath=null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: this method will always return an array of fields objects, even if only one is found (for more consistency), and this is the main difference with fetchFieldStruct(null).&lt;br /&gt;
&lt;br /&gt;
== Managing custom fields records ==&lt;br /&gt;
&lt;br /&gt;
Now that you know how to create and manage the structure of custom fields, we can proceed on to managing the records.&lt;br /&gt;
&lt;br /&gt;
=== Creating a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Simply use the create() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;create($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
$object must be an object containing as its properties the customfields data.&lt;br /&gt;
&lt;br /&gt;
For example, if you have a field named mycustomfield, your object must have $object-&amp;gt;mycustomfields defined.&lt;br /&gt;
&lt;br /&gt;
This method will store in the database ALL the custom fields that were created/edited and with a valid value (eg: an empty value for a field that does not accept null value is invalid) AT ONCE! So you can modify all the columns of one record at once, but not several records at once.&lt;br /&gt;
&lt;br /&gt;
Concretely, this is very useful with Dolibarr, because modules tends to store everything inside a single object generally called $object, and CustomFields tries to hook onto $object, so that you can just pass $object as is to create() and it will save all the custom fields.&lt;br /&gt;
&lt;br /&gt;
But you can also create an empty object with $object = new stdClass(); and then manually fill the custom fields records with whatever you want, and then call the create() method, this will work as well!&lt;br /&gt;
&lt;br /&gt;
You can also just modify only one column of one record, by just specifying one custom field column_name in your $object. The other custom fields that are not specified won&#039;t be created/modified.&lt;br /&gt;
&lt;br /&gt;
=== Updating a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Use the update() method, which is an alias for the create() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;update($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As noted above, update() is an alias of the create() method, which means that CustomFields will try to detect by itself if a record needs to be updated or created.&lt;br /&gt;
&lt;br /&gt;
=== Deleting a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Use the delete() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;delete($id); // where $id is = to fk_moduleid column, NOT rowid&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fetching a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Just use the fetch() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$records = $customfields-&amp;gt;fetch($id=null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where $id can either be = fk_moduleid (NOT rowid), or null to fetch all the records at once.&lt;br /&gt;
&lt;br /&gt;
This method will either return null, a single record object, or an array of records objects.&lt;br /&gt;
&lt;br /&gt;
=== Fetching all custom fields&#039; records ===&lt;br /&gt;
&lt;br /&gt;
Use the fetchAll() method, which is an alias for fetch():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$records = $customfields-&amp;gt;fetchAll();&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: this method will always return an array of records object, for more consistency (and this is the main difference with fetch(null) ).&lt;br /&gt;
&lt;br /&gt;
=== Fetching referenced records of a constrained field ===&lt;br /&gt;
&lt;br /&gt;
A constrained custom field points to another database table, and a value for this constrained field is the rowid of a record in the referenced table.&lt;br /&gt;
&lt;br /&gt;
Thus, the principal purpose of a constrained custom field is not the value of the field in itself (it&#039;s simply an integer), but rather the other fields available from the referenced table, the constrained field being in fact just a pointer.&lt;br /&gt;
&lt;br /&gt;
To fetch the remote record from the referenced table, use the fetchReferencedValuesList() of the CustomFields class:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$fkrecord = fetchReferencedValuesList($field, $id=null, $where=null, $allcolumns=false);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
&lt;br /&gt;
* $field is a custom field object (fetched using fetchFieldStruct() or fetchAllFieldsStruct()),&lt;br /&gt;
* $id is the rowid of the record to get, just like fetch() and fetchAll(). Can be null to fetch all remote records (or to use the $where clause).&lt;br /&gt;
* $where is a string without the WHERE clause (eg: &amp;quot;fk_facture=1&amp;quot;) to get finer control. In this case, you can use $id or set $id=null depending on your purpose. This is mainly a private argument to allow for cascade option.&lt;br /&gt;
* $allcolumns if false, it will fetch only the required columns to do the smart value substitution. If true, it will fetch all remote columns (useful for ODT substitution). If an array of strings, each string will be a column to fetch from the remote database.&lt;br /&gt;
&lt;br /&gt;
Note that this function is NOT recursive (it will fetch the records from the referenced table, and that&#039;s all, it won&#039;t fetch the custom fields of the referenced table nor recursively fetch the remote constrained custom fields). To do recursive fetching, see in the Facade API.&lt;br /&gt;
&lt;br /&gt;
Note2: this function automatically manages Smart Value Substitution depending of the $field-&amp;gt;column_name.&lt;br /&gt;
&lt;br /&gt;
=== Recursive fetching referenced records of a constrained field ===&lt;br /&gt;
&lt;br /&gt;
If you want to recursively fetch a constrained field (eg: constrainedfield1-&amp;gt;ref_table-&amp;gt;constrainedfield2-&amp;gt;ref_table2-&amp;gt;...), you need another function called fetchReferencedValuesRec(). Technically, this function is in the Facade API (inside /customfields/lib/customfields_aux.lib.php), but it isn&#039;t meant to be used by lambda users (contrary to customfields_fill_object()), but is mainly a helper function for the rest of the facade API.&lt;br /&gt;
&lt;br /&gt;
fetchReferencedValuesRec() works very similarly to fetchReferencedValuesList(), but it will work recursively.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$fkrecord = fetchReferencedValuesRec($customfields, $field, $id, $recursive=true)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where $customfields is the CustomFields instance for the module you are working on, $field is the constrained field you want to fetch, $id is the record&#039;s id you want to fetch, and $recursive enables or disables the recursion.&lt;br /&gt;
&lt;br /&gt;
Note that this function automatically manages Smart Value Substitution (and also recursively).&lt;br /&gt;
&lt;br /&gt;
Note2: This function tries to automatically avoid an infinite recursion loop by using a $blacklist: for a given constrained custom field, a table won&#039;t be visited twice. This means that for example you can do: invoice-&amp;gt;user-&amp;gt;socpeople-&amp;gt;invoice, this will work and every referenced fields will be fetched, but it will automatically stop the recursion at the second invoice, because else it could continue in a loop to user, socpeople, invoice, user, socpeople, etc.&lt;br /&gt;
&lt;br /&gt;
Note3: when adding recursively referenced fields, the key in the returned array will be prefixed by the name of the custom field that led to those remote fields. Thus, you can have something like &amp;quot;cf_myfield1_cf_remotefield1_cf_remotefield2&amp;quot; if you have a constrained field cf_myfield1-&amp;gt;cf_remotefield1-&amp;gt;cf_remotefield2.&lt;br /&gt;
&lt;br /&gt;
== Input forms for custom fields ==&lt;br /&gt;
&lt;br /&gt;
The API provides some neat ways to automatically manage the input forms for users to input new values, with the best adapted HTML form based on the custom field&#039;s data type.&lt;br /&gt;
&lt;br /&gt;
The following methods will print input fields that are best adapted to the SQL data type of your custom fields as well as figure out the possible values and even format them (for example: a not null enum will be printed as a HTML select list without an empty option; fetch automatically remote columns for constrained fields and show them in a select).&lt;br /&gt;
&lt;br /&gt;
So you don&#039;t have to bother how your data will be presented to the user nor how it will be processed.&lt;br /&gt;
&lt;br /&gt;
=== Printing an input form adapted to the field&#039;s data type ===&lt;br /&gt;
&lt;br /&gt;
showInputField() is a sort of converter customfieldstructure -&amp;gt; HTML input field.&lt;br /&gt;
&lt;br /&gt;
Use the &#039;&#039;&#039;showInputField()&#039;&#039;&#039; method to print the right input form for a custom field.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;showInputField($field, $currentvalue=null, $moreparam=&#039;&#039;, $ajax_php_callback=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
showInputField() takes 4 parameters:&lt;br /&gt;
&lt;br /&gt;
* $field is a CustomFields structure object (eg: returned by fetchFieldStruct()) and is necessary to detect the right datatype and the possible values.&lt;br /&gt;
* $currentvalue is the current value if you want to preselect a value (for example if the user already defined a value, you can reload it here). This is optional, in case it is not defined, either a null/empty value will be chosen, or either the default value defined when creating the custom field.&lt;br /&gt;
* $moreparam is used to add more HTML attributes, if you want for example to add some css styling or whatever you want.&lt;br /&gt;
* $ajax_php_callback is optional and allows to specify the relative url to the php callback script. If specified, an AJAX script will be automatically attached to this field (see AJAX-PHP callback below).&lt;br /&gt;
&lt;br /&gt;
showInputField() only prints ONE HTML input field, thus you have to enclose it/them by your own HTML form.&lt;br /&gt;
&lt;br /&gt;
eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print &#039;&amp;lt;form method=&amp;quot;get&amp;quot; action=&amp;quot;?&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
print &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;customtoken&amp;quot; value=&amp;quot;ABCDEFGH&amp;quot; /&amp;gt;&#039;; // example of a custom hidden HTML input field, that can be used to pass a token for example&lt;br /&gt;
// Loop over each custom fields (you have to load them before with $customfields-&amp;gt;fetchAllFieldsStruct()&lt;br /&gt;
foreach ($customfields-&amp;gt;fields as $field) {&lt;br /&gt;
    print $customfields-&amp;gt;showInputField($field, $currentvalue=null, $moreparam=&#039;&#039;);&lt;br /&gt;
}&lt;br /&gt;
print &#039;&amp;lt;/form&amp;gt;&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can also store the resulting HTML input field and process it by a regexp for example:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$myhtmlfield = $customfields-&amp;gt;showInputField($field, $currentvalue=null, $moreparam=&#039;&#039;);&lt;br /&gt;
preg_replace($myhtmlfield, ..., ...);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: for constrained fields, if the name follows the convention of &#039;&#039;&#039;Smart Value Substitution&#039;&#039;&#039; (ie: remote columns names are included in the custom field&#039;s name), showInputField() will &#039;&#039;&#039;automatically fetch the remote values, format them, and present them&#039;&#039;&#039; in a HTML select.&lt;br /&gt;
&lt;br /&gt;
=== Printing an input form for a custom field ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;showInputForm()&#039;&#039;&#039; is a function used to print a single (and only one!) custom field with an adapted HTML input field, but enclosed by a HTML form. This is used for example when you want to allow to edit a single custom field but not the others.&lt;br /&gt;
&lt;br /&gt;
showInputForm() is in fact a wrapper for showInputField(), thus it will call showInputField() and simply enclose it by a form.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;showInputForm($id, $field, $currentvalue=null, $idvar=&#039;id&#039;, $page=null, $moreparam=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== AJAX-PHP callback ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;showInputFieldAjax()&#039;&#039;&#039; will output an AJAX script (using jQuery) to send and receive data to and from a specified php script. The AJAX script will automatically manage HTML form input&#039;s updating as long as the received data follows some convention.&lt;br /&gt;
&lt;br /&gt;
An AJAX script can be attached to any field (including non-CustomFields) by using the method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;showInputFieldAjax($id, $php_callback_url, $on_func=&amp;quot;change&amp;quot;, $request_type=&amp;quot;post&amp;quot;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
* $id is the HTML id of the field you want to attach the AJAX to.&lt;br /&gt;
* $phpcallback the relative path from Dolibarr&#039;s htdocs root folder to the php script that will receive and send back data.&lt;br /&gt;
* $on_func is the Javascript or jQuery event that should trigger the AJAX script (by default on change).&lt;br /&gt;
* $request_type is the HTML query type that will be used to send the data to the PHP callback (either &amp;quot;get&amp;quot; or &amp;quot;post&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
The AJAX script will automatically manage sending/receiving of data:&lt;br /&gt;
&lt;br /&gt;
* automatically send all form&#039;s inputs values, as well as the current calling field&#039;s name and value. The sending happens on change by default, but another event can be set using the $on_event argument. Data will be sent using standard GET or POST.&lt;br /&gt;
* Upon reception of data from the php script, the AJAX script will automatically parse the data and update the HTML fields.&lt;br /&gt;
&lt;br /&gt;
The PHP callback script must send back to AJAX the data in JSON encoded format, and with with a specific format:&lt;br /&gt;
&lt;br /&gt;
* the data must be an associative array, where each entry&#039;s key is the name (not ID!) of an HTML field to update.&lt;br /&gt;
* each entry&#039;s value is an associative array, where each sub-entry&#039;s key is the action to do. Available actions: &#039;options&#039;, &#039;value&#039;, &#039;html&#039;, &#039;alert&#039;.&lt;br /&gt;
* each sub-entry&#039;s value is the value for this action and HTML field.&lt;br /&gt;
&lt;br /&gt;
Thus, this approach allows to generically support a wide range of updating actions, managed automatically.&lt;br /&gt;
&lt;br /&gt;
You, the implementer, have just to edit the PHP callback script and return an associative array defining what action you want for each field you want to update. This is exactly what has been implemented in the Custom AJAX Functions library, which simplifies even more your job by managing automatically the step described here (generating AJAX and formatting data), so that you can focus on &#039;&#039;&#039;making the data&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Printing custom fields in documents ==&lt;br /&gt;
&lt;br /&gt;
The API can also automatically manage the printing and formatting of custom fields in your ODT, PDF and HTML documents.&lt;br /&gt;
&lt;br /&gt;
=== Printing in HTML ===&lt;br /&gt;
&lt;br /&gt;
You can either just print the value as-is, or pass them through the printField() or simpleprintField() function to format them (very useful for constrained fields) - see below.&lt;br /&gt;
&lt;br /&gt;
=== Printing in documents ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;printField()&#039;&#039;&#039; is used to properly print and translate a custom field&#039;s record&#039;s value. Generally, you can just print the custom field&#039;s value as-is, but in a few cases (eg for boolean types, or constrained fields), using the printField() method will more properly print the value.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;printField($field, $value, $outputlangs=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
* $field is the custom field&#039;s SQL structure as an object (eg: returned by fetchFieldStruct()).&lt;br /&gt;
* $value is the custom field&#039;s value for the current record (eg: $customfields-&amp;gt;fields-&amp;gt;myfield).&lt;br /&gt;
* $outputlangs is the Translate object for the target language.&lt;br /&gt;
&lt;br /&gt;
For example, with a constrained field with Smart Value Substitution, instead of printing the id like 1, it will print the full name of user like FirstName Name.&lt;br /&gt;
&lt;br /&gt;
This method will also translate a value if a translation is found in the language files.&lt;br /&gt;
&lt;br /&gt;
Another method &#039;&#039;&#039;printFieldPDF()&#039;&#039;&#039; can be used to properly print a value for a PDF (properly encoding the characters).&lt;br /&gt;
&lt;br /&gt;
=== Simple printing in documents ===&lt;br /&gt;
&lt;br /&gt;
If you don&#039;t want to have to manually fetch the structure of every custom field whose you want to print the value, you can then use a simplifier method called &#039;&#039;&#039;simpleprintField()&#039;&#039;&#039; which will take as input a custom field name instead of a custom field SQL structure object.&lt;br /&gt;
&lt;br /&gt;
This method is just a simplifier (Facade) wrapper for the printField() method, and thus will have the same advantages (fetch constrained fields, values translation, etc.).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;simpleprintField($fieldname, $value, $outputlangs=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
* $fieldname is the custom field&#039;s name (eg: &amp;quot;myfield&amp;quot;).&lt;br /&gt;
* $value is the custom field&#039;s value for the current record (eg: $customfields-&amp;gt;fields-&amp;gt;myfield).&lt;br /&gt;
* $outputlangs is the Translate object for the target language.&lt;br /&gt;
&lt;br /&gt;
Another method &#039;&#039;&#039;simpleprintFieldPDF()&#039;&#039;&#039; can be used to properly print a value for a PDF (properly encoding the characters).&lt;br /&gt;
&lt;br /&gt;
=== Find the label ===&lt;br /&gt;
&lt;br /&gt;
Finding the label of a custom field might not be as simple as it seems. First, values of properties objects may be easily used, but their name (key) is a worst job. Secondly, custom fields&#039; labels can be translated, and the following functions are expressly made for that purpose. Thirdly, for PDF and some other documents you have to encode the characters if you want them to properly be drawn.&lt;br /&gt;
&lt;br /&gt;
So if you want to print a custom field&#039;s label with translation (multilingual) support, just use the &#039;&#039;&#039;findLabel()&#039;&#039;&#039; method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$translatedfieldname = $customfields-&amp;gt;findLabel($fieldname, $outputlangs = &#039;&#039;); // where $fieldname is just a custom field label (eg: cf_mycustomfield or mycustomfield, both will work) and $outputlangs is the target language object (see the Translate class native of Dolibarr)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As for the other printing methods, you also have the &#039;&#039;&#039;findLabelPDF()&#039;&#039;&#039; wrapper method to easily print labels that with characters encoded for PDF.&lt;br /&gt;
&lt;br /&gt;
= Facade API =&lt;br /&gt;
&lt;br /&gt;
The Facade API was made to ease the usage of the CustomFields class with the Dolibarr system. It provides a neat way to do many operations with single commands, and will automatically manage the loading of the CustomFields class, and its usage with many optimizations.&lt;br /&gt;
&lt;br /&gt;
== customfields_fill_object() ==&lt;br /&gt;
&lt;br /&gt;
You can load the Facade API and then use the &#039;&#039;&#039;customfields_fill_object()&#039;&#039;&#039; to automatically fetch the fields structures and the records, and also automatically link to remote fields when a constrained field is found. This is an all-in-one solution to easily fetch CustomFields datas, and is also optimized to fully use caching and other special properties of the CustomFields class.&lt;br /&gt;
&lt;br /&gt;
Concretely, you just have to include the Facade API, then pass the customfields_fill_object() with a Dolibarr $object, and the Facade API will return a CustomFields object with appended the custom fields records and also the custom fields&#039; SQL structures.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$customfields = customfields_fill_object($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* $object will then include the custom fields records associated with this object (eg: $object-&amp;gt;customfields-&amp;gt;cf_yourfield).&lt;br /&gt;
* $customfields will be a CustomFields object containing the fields structures (eg: $customfields-&amp;gt;fields-&amp;gt;yourfield).&lt;br /&gt;
&lt;br /&gt;
You can also call any CustomFields function if you need to using the $customfields object, eg: $customfields-&amp;gt;simpleprintField($object-&amp;gt;customfields-&amp;gt;cf_yourfield);&lt;br /&gt;
&lt;br /&gt;
Note: $object can also be a dummy object containing only two properties that are required: $object-&amp;gt;id and $object-&amp;gt;table_element.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will now describe the full specification of this function:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$customfields = customfields_fill_object(&amp;amp;$object,$fromobject = null, $outputlangs = null, $prefix = null,$pdfformat = false);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* $object is the object where the customfields will be saved&lt;br /&gt;
* $fromobject is the object from where the customfields will be fetched (this can be a different module than $object, or can be null to be the same as $object)&lt;br /&gt;
* $outputlangs is the target language for translation of fields value (if empty, the default current global translation object will be used)&lt;br /&gt;
* $prefix allows you to save the custom fields in a sub-object. By default, when $prefix=null, custom fields are saved in the path $object-&amp;gt;customfields, but when you specify a prefix, custom fields will be saved inside $object-&amp;gt;customfields-&amp;gt;$prefix. This may be useful if you want to store multiple object&#039;s custom fields or records in the same $object.&lt;br /&gt;
* $pdfformat defines if you want a special formatting for the values: if null, there&#039;s no formatting, if false, there is a simple formatting (eg: translation of fields&#039; values), if true then the formatting will be adapted for PDF templating.&lt;br /&gt;
&lt;br /&gt;
For more informations on pratical usages of this function, please [[Module_CustomFields#Implementing_in_php_code_.28dolibarr_core_modules_or_your_own_module.29|read the user documentation on how to implement CustomFields in php code]] or [[Module_CustomFields_Cases#Linking_Dolibarr_objects_from_two_different_modules|read an example case of linking two objects of different modules together]].&lt;br /&gt;
&lt;br /&gt;
== customfields_fill_object_lines() ==&lt;br /&gt;
&lt;br /&gt;
Similar to customfields_fill_object() for normal custom fields, there is also a &#039;&#039;&#039;customfields_fill_object_lines()&#039;&#039;&#039; to fetch custom fields associated with lines of products/services. This function is also optimized and it is strongly advised to use it and to not try to fetch manually this kind of custom fields (ie: associated with lines of products/services rather than objects), because there are a lot of optimizations going on behind the scene and that are very hard to do manually.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields for lines of products/services (you can then access them by doing $object-&amp;gt;customfields-&amp;gt;lines-&amp;gt;$lineid-&amp;gt;cf_yourfield)&lt;br /&gt;
$customfields = customfields_fill_object_lines($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Requirements: $object can be a dummy object but must contain at least 2 properties: $object-&amp;gt;id and $object-&amp;gt;table_element_line.&lt;br /&gt;
&lt;br /&gt;
For more informations on pratical usages of this function, please [[Module_CustomFields#Implementing_in_php_code_.28dolibarr_core_modules_or_your_own_module.29|read the user documentation on how to implement CustomFields in php code]].&lt;br /&gt;
&lt;br /&gt;
= Architecture =&lt;br /&gt;
Here is a full list of the CustomFields packaged files with a short description (for a more in-depth view just crawl the source files, they are full of comments):&lt;br /&gt;
&lt;br /&gt;
== Core files ==&lt;br /&gt;
files that are necessary for the CustomFields to work, they contains the core functions&lt;br /&gt;
&lt;br /&gt;
* /customfields/admin/customfields.php --- Administrator&#039;s configuration panel : this is where you create and manage the custom fields definitions&lt;br /&gt;
* /customfields/class/actions_customfields.class.php --- Hooks class : used to hook into Dolibarr core modules without altering any core file (can be used to hook into your own modules too). Also used to preprocess the parameters before calling the printforms library.&lt;br /&gt;
* /customfields/class/customfields.class.php --- Core class : every database action is made here in this class. You can find some printing functions because they are very generic.&lt;br /&gt;
* /customfields/conf/conf_customfields.lib.php --- Configuration file : contains the main configurable variables to adapt CustomFields to your needs or to expand its support to other modules and more native sql types.&lt;br /&gt;
* /customfields/conf/conf_customfields_func.lib.php --- Auxiliary functions library to read and manage config variables&lt;br /&gt;
* /customfields/core/modules/modCustomFields.class.php --- Dolibarr&#039;s module definition file : this is a core file necessary for Dolibarr to recognize the module and to declare the hooks to Dolibarr (but it does not store anything else than meta-informations).&lt;br /&gt;
* /customfields/core/substitutions/functions_customfields.lib.php --- CustomFields substitution class for ODT generation : necessary to support customfields tags in ODT files&lt;br /&gt;
* /customfields/core/triggers/interface_50_modCustomFields_SaveFields.class.php --- Core triggers file : this is where the actions on records are managed. This is an interface between other modules and CustomFields management. You should not modify this file but you should add your triggers in $triggersarray inside conf_customfields.lib.php, but if you really have a special need and know what you do, you can edit this file to directly add your own triggers. Also, if CustomFields are shown in the forms but cannot be modified nor saved, then probably this trigger file is not detected by Dolibarr (maybe you can try to lower the number 50 to raise the priority?).&lt;br /&gt;
* /customfields/fields/customfields_fields_extend.lib.php.example --- Overloading functions library with users functions: this is where implementers can use their own php code to manage the custom fields they want (one function per action and per field).&lt;br /&gt;
* /customfields/fields/customfields_fields_ajax_custom.lib.php.example --- Custom AJAX functions library with users functions: this is where implementers can use their own php code to manage the custom fields cascading and AJAX calls, similarly to the Overloading functions library (one function per action and per field).&lt;br /&gt;
* /customfields/langs/code_CODE/customfields.lang --- Core language file : this is where you can translate the admin config panel (data types names, labels, descriptions, etc.)&lt;br /&gt;
* /customfields/langs/code_CODE/customfields-user.lang --- User defined language file : this is where you can store the labels and values of your custom fields (see the related chapter)&lt;br /&gt;
* /customfields/lib/customfields_aux.lib.php --- Simplifier library to easily use CustomFields class to populate an $object with custom fields datas (Facade design pattern). This is what you should use for most of your needs.&lt;br /&gt;
* /customfields/lib/customfields_printforms.lib.php --- Core printing library for records : contains only printing functions, there&#039;s not really any core functions inside but it allows to manage the printing of the custom fields records and their editing.&lt;br /&gt;
* /customfields/lib/customfields_ajax_wrapper.lib.php --- AJAX wrapper library to automatically manage cascading at objects creation. This isn&#039;t really necessary for CustomFields to function, this is more an addition to allow dynamical AJAX updating, but CustomFields core functionalities can work without this library.&lt;br /&gt;
* /customfields/sql/*.sql --- Unused (the tables are created directly via a function in the customfields.class.php) - but the sql files are still valid, they were used as squeletton in the first designs.&lt;br /&gt;
&lt;br /&gt;
== PDFTest module ==&lt;br /&gt;
optional module to append a page listing all custom fields values on newly generated PDF documents. To be used to test if custom fields are working correctly before making one&#039;s own template.&lt;br /&gt;
&lt;br /&gt;
* /customfieldspdftest/class/actions_customfieldspdftest.class.php --- Main functions library and hooks class, to hook onto pdf generation. Contains the PDFTest functions.&lt;br /&gt;
* /customfieldspdftest/core/modules/modCustomFieldsPDFTest.class.php --- Module descriptor for Dolibarr&lt;br /&gt;
&lt;br /&gt;
== Runtime outline ==&lt;br /&gt;
&lt;br /&gt;
Here is an outline of how CustomFields works: what functions will be called and why:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At admin page:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* No external function call (apart for some icons or needed admin functions like for tabs), everything that is done are sql queries via CustomFields class (to fetch and modify the sql columns in the customfields tables).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At printing:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Module&#039;s hook formObjectOptions calls Dolibarr&#039;s hookmanager&lt;br /&gt;
* Hookmanager calls CustomFields /class/actions_customfields.class.php, function formObjectOptions() of CustomFields&lt;br /&gt;
* Preprocess a few parameters (mainly the $action) and calls customfields_print_forms()&lt;br /&gt;
* customfields_print_forms() preprocess all the hook&#039;s parameters and assimilate them with the CustomFields&#039;s configuration parameters (there&#039;s no connection to the database at this stage, only parameters preprocessing), then calls /lib/customfields_printforms.lib.php, functions customfields_print_creation_form() or customfields_print_datasheet_form&lt;br /&gt;
* both customfields_print_creation_form() lookup the database using CustomFields class and prints empty custom fields; customfields_print_datasheet_form() lookup the database using CustomFields class, fetch the values of the custom fields and prints them;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At edition:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Same steps as &#039;&#039;&#039;At printing&#039;&#039;&#039;&lt;br /&gt;
* then customfields_print_datasheet_form() process the custom field being edited differently than the others: if editing, it prints the html input field (to enter a new value), or if saving it just saves the data into the database by calling the CUSTOMFIELDS_MODIFY trigger and then continue to print the other fields normally.&lt;br /&gt;
* CUSTOMFIELDS_MODIFY trigger calls $customfields-&amp;gt;update($newrecord);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At creation (saving):&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Module&#039;s creation trigger MYMODULE_CREATE calls Dolibarr&#039;s run_trigger()&lt;br /&gt;
* Dolibarr run_trigger() calls CustomFields trigger file /core/trigger/interface_50_modCustomFields_SaveFields.class.php, function run_trigger()&lt;br /&gt;
* CustomFields&#039;s run_trigger() assimilate the given parameters with the CustomFields&#039;s config parameters, and if the trigger is found in $triggersarray, then the appropriate action is taken (by default: _CREATE or another standard action if recognized), if not in $triggersarray, the function will still try to automatically detect the appropriate action by detecting MYMODULE_ALISTOFACTIONS with a regular expression.&lt;br /&gt;
* The appropriate action is then done (creation in this case) by calling $customfields-&amp;gt;create($record).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At edition or creation of product lines:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Similar to &#039;&#039;&#039;At edition&#039;&#039;&#039; but with hooks: formCreateProductOptions and formEditProductOptions.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At ODT document generation and substitution:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Module&#039;s ODT generator calls for Dolibarr&#039;s function complete_substitution_array(), which then calls for CustomFields substitution file /core/substitution/functions_customfields.lib.php, function customfields_completesubstitutionarray()&lt;br /&gt;
* CustomFields&#039;s function customfields_completesubstitutionarray() calls the simplifier library customfields_aux.lib.php, function customfields_fill_object() and make the substitution inside the $substitutionarray.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At ODT document generation and substitution for product lines:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Same as the previous point but this function is called for every product lines, and thus the data is cached. Also the simplifier function is here called customfields_fill_object_lines(), and the CustomFields&#039;s substitution function called customfields_completesubstitutionarray_lines().&lt;br /&gt;
&lt;br /&gt;
= How to add the support of a new module =&lt;br /&gt;
&lt;br /&gt;
There are two cases: either the module already implements hooks, and you just have to configure CustomFields to support it, or either the module does not implements hooks nor triggers.&lt;br /&gt;
&lt;br /&gt;
This chapter will guide you through the steps to make any module supported by CustomFields (be it a Dolibarr core module or your own module).&lt;br /&gt;
&lt;br /&gt;
== Preliminary test ==&lt;br /&gt;
&lt;br /&gt;
This preliminary test will allow you to know at a glance how much work needs to be done in order to make your module supported by CustomFields, and it will also give you all the config parameters if they are available.&lt;br /&gt;
&lt;br /&gt;
Important note: this test will only give data about hooks, which is the main part to make CustomFields work for your module. But for triggers, there is no way to test, so you will have to check the triggers manually (either by testing if the custom fields are saved at creation page, or by looking at the source code of the objects classes).&lt;br /&gt;
&lt;br /&gt;
Edit the php script of the module (check the URL when you access the module&#039;s datasheet or creation page - attention both are two different pages, so you have to do this twice), then put this at the very &#039;&#039;&#039;end&#039;&#039;&#039; of the php script:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// CustomFields debuglines to show config parameters for current module&lt;br /&gt;
print(&amp;quot;&amp;lt;pre&amp;gt;&amp;quot;); // html nice output&lt;br /&gt;
print(&amp;quot;table_element: {$object-&amp;gt;table_element} - table_element_line: {$object-&amp;gt;table_element_line} - contexts: &amp;quot;);&lt;br /&gt;
print_r($hookmanager-&amp;gt;contextarray);&lt;br /&gt;
print(&amp;quot; - conf hooks_modules: &amp;quot;);&lt;br /&gt;
print_r($conf-&amp;gt;hooks_modules);&lt;br /&gt;
print(&amp;quot; - hooks attached: &amp;quot;);&lt;br /&gt;
print_r($hookmanager-&amp;gt;hooks);&lt;br /&gt;
print(&amp;quot; - user rights: &amp;quot;);&lt;br /&gt;
print_r($user-&amp;gt;rights);&lt;br /&gt;
print(&amp;quot;&amp;lt;/pre&amp;gt;&amp;quot;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now reload the page, and scroll at the very bottom. You should see some informations about the module.&lt;br /&gt;
&lt;br /&gt;
Depending on the data available, you will know what you have to do:&lt;br /&gt;
&lt;br /&gt;
* If all the datas is available, then it&#039;s ok, jump to [[Module_CustomFields_Dev#Configuring_CustomFields_to_support_the_module]] and copy these datas to the parameters of conf_customfields.lib.php&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;table_element&#039;&#039;&#039;: $object is not defined, try to find how is called the main object variable and replace $object by this variable in the debug code given above (eg: in Agenda, the object variable is called $agenda and $agendacomm).&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;table_element_line&#039;&#039;&#039;: either there&#039;s no products lines for this module, either the main object variable is not called $object (see the previous point). One last case is that the object is not standard and does not store table_element_line (the name of the table that stores its products lines), in which case you have to manually add it in the class (eg: $this-&amp;gt;table_element_line = &#039;something&#039;).&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;contexts&#039;&#039;&#039;: the hookmanager is not defined, and probably there&#039;s no hook at all in this module, so you&#039;ll have to implement them by yourself. Jump to [[Module_CustomFields_Dev#Hooks]].&lt;br /&gt;
&lt;br /&gt;
* If there is a list of &#039;&#039;&#039;contexts&#039;&#039;&#039; but the module cannot be found in the list: the hookmanager is defined but the module has not defined a context, you still have to implement the hooks. Jump to [[Module_CustomFields_Dev#Hooks]].&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;conf hooks_modules&#039;&#039;&#039;: this variable is not necessary, empty or not it doesn&#039;t matter. But if you see customfields and/or customfieldspdftest inside the list, it is a confirmation that CustomFields indeed is hooking the module correctly.&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;hooks attached&#039;&#039;&#039;, or CustomFields cannot be found in the list: CustomFields has not been properly configured to hook to this module, or you forgot to disable then re enable CustomFields in order to update the CustomFields&#039;s hooks. Jump to [[Module_CustomFields_Dev#Configuring_CustomFields_to_support_the_module]].&lt;br /&gt;
&lt;br /&gt;
* If there is no &#039;&#039;&#039;$user-&amp;gt;rights&#039;&#039;&#039;: there is either no standard security implemented in this module, or either the variable is called with another name. If there is no security, you don&#039;t need to configure it in CustomFields, but if there is a security, you need to find how the variable is called (like $object can get another name sometimes).&lt;br /&gt;
&lt;br /&gt;
* If there is &#039;&#039;&#039;$user-&amp;gt;rights&#039;&#039;&#039; but the module can&#039;t be found in the list: either the name for the rights is different (this often happens, the rights array is not yet standardized), or either the rights are hardcoded into the module&#039;s code (rights should be announced to the $user-&amp;gt;rights array, but sometimes it happens that the dev forgot to do so, and thus the right is entirely hardcoded and you can&#039;t find it in the dynamic rights array). In any of these two cases, you will have to manually read the module&#039;s code to find the right. Also, maybe there is no security implemented, but since $user-&amp;gt;rights is declared this is less probable.&lt;br /&gt;
&lt;br /&gt;
Here is an example of output on a correctly hooked module (invoice):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
table_element: facture - table_element_line: facturedet - contexts: Array&lt;br /&gt;
(&lt;br /&gt;
    [0] =&amp;gt; searchform&lt;br /&gt;
    [1] =&amp;gt; leftblock&lt;br /&gt;
    [2] =&amp;gt; toprightmenu&lt;br /&gt;
    [3] =&amp;gt; invoicecard&lt;br /&gt;
)&lt;br /&gt;
 - conf hooks_modules: Array&lt;br /&gt;
(&lt;br /&gt;
    [customfields] =&amp;gt; invoicecard:propalcard:productcard:ordercard:thirdpartycard:contactcard:ordersuppliercard:invoicesuppliercard:membercard:actioncard:projectcard:projecttaskcard:contractcard:interventioncard:doncard:tripsandexpensescard:taxvatcard:expeditioncard:invoicecard:propalcard:ordercard:ordersuppliercard:invoicesuppliercard&lt;br /&gt;
    [customfieldspdftest] =&amp;gt; Array&lt;br /&gt;
        (&lt;br /&gt;
            [0] =&amp;gt; pdfgeneration&lt;br /&gt;
            [1] =&amp;gt; ordersuppliercard&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
)&lt;br /&gt;
 - hooks attached: Array&lt;br /&gt;
(&lt;br /&gt;
    [invoicecard] =&amp;gt; Array&lt;br /&gt;
        (&lt;br /&gt;
            [customfields] =&amp;gt; ActionsCustomFields Object&lt;br /&gt;
                (&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
)&lt;br /&gt;
 - user rights: stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [user] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [user] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [lire] =&amp;gt; 1&lt;br /&gt;
                    [creer] =&amp;gt; 1&lt;br /&gt;
                    [password] =&amp;gt; 1&lt;br /&gt;
                    [supprimer] =&amp;gt; 1&lt;br /&gt;
                    [export] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [self] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [creer] =&amp;gt; 1&lt;br /&gt;
                    [password] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [user_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [readperms] =&amp;gt; 1&lt;br /&gt;
                    [write] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [self_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [readperms] =&amp;gt; 1&lt;br /&gt;
                    [writeperms] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [group_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [read] =&amp;gt; 1&lt;br /&gt;
                    [readperms] =&amp;gt; 1&lt;br /&gt;
                    [write] =&amp;gt; 1&lt;br /&gt;
                    [delete] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    [facture] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [lire] =&amp;gt; 1&lt;br /&gt;
            [creer] =&amp;gt; 1&lt;br /&gt;
            [invoice_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [unvalidate] =&amp;gt; 1&lt;br /&gt;
                    [send] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [valider] =&amp;gt; 1&lt;br /&gt;
            [paiement] =&amp;gt; 1&lt;br /&gt;
            [supprimer] =&amp;gt; 1&lt;br /&gt;
            [facture] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [export] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    [propale] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [lire] =&amp;gt; 1&lt;br /&gt;
            [creer] =&amp;gt; 1&lt;br /&gt;
            [valider] =&amp;gt; 1&lt;br /&gt;
            [propal_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [send] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [cloturer] =&amp;gt; 1&lt;br /&gt;
            [supprimer] =&amp;gt; 1&lt;br /&gt;
            [export] =&amp;gt; 1&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    [produit] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [lire] =&amp;gt; 1&lt;br /&gt;
            [creer] =&amp;gt; 1&lt;br /&gt;
            [supprimer] =&amp;gt; 1&lt;br /&gt;
            [export] =&amp;gt; 1&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    etc... a lot of other rights...&lt;br /&gt;
&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When you are finished with implementing the support of CustomFields in the module, you should delete these debug lines.&lt;br /&gt;
&lt;br /&gt;
== Implementing the support of CustomFields in the module ==&lt;br /&gt;
&lt;br /&gt;
If there is a module you want to implement the support of CustomFields, but does not yet has the required components, here is a guide on how to implement these components.&lt;br /&gt;
&lt;br /&gt;
=== Hooks ===&lt;br /&gt;
&lt;br /&gt;
Implementing hooks will allow custom fields to appear on your module&#039;s page. They will also be editable and they will save the data. For creation, you need to use a trigger (see the subchapter below).&lt;br /&gt;
&lt;br /&gt;
First you should be familiar with the way that hooks are implemented in modules. If you don&#039;t, please first [[Hooks_system#Implementation|read the hooks documentation]].&lt;br /&gt;
&lt;br /&gt;
An example case of implementation of hooks into a Dolibarr core file can be found in the [[Module_CustomFields_Cases|CustomFields Example Cases wiki page]].&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Datasheet hook&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
To implement CustomFields in your module, you must initialize the hook manager, then use the following hook context:&lt;br /&gt;
* formObjectOptions&lt;br /&gt;
&lt;br /&gt;
This hook must be placed everywhere in your code where you want custom fields to appear (eg: on the creation page and datasheet view page).&lt;br /&gt;
&lt;br /&gt;
Note: Generally, you will have to place the hook just before the end of a table tag (&#039;&#039;&#039;&amp;lt;/table&amp;gt;&#039;&#039;&#039;), so if you don&#039;t know where to place it (eg: if you&#039;re implementing CustomFields on a Dolibarr core module), just search for this tag in the page shown in the url (eg: /htdocs/commande/fiche.php).&lt;br /&gt;
&lt;br /&gt;
Eg: First load the hookmanager at the beginning of your php script:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Initialize technical object to manage hooks of thirdparties. Note that conf-&amp;gt;hooks_modules contains array array&lt;br /&gt;
include_once(DOL_DOCUMENT_ROOT.&#039;/core/class/hookmanager.class.php&#039;);&lt;br /&gt;
$hookmanager=new HookManager($db);&lt;br /&gt;
$hookmanager-&amp;gt;initHooks(array(&#039;yourmodule&#039;));&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then place the hook as many times as required:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// ... a lot of php code ...&lt;br /&gt;
&lt;br /&gt;
print &#039;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&#039;; // end of the last row of a table&lt;br /&gt;
&lt;br /&gt;
// Other attributes&lt;br /&gt;
$parameters=array(&#039;colspan&#039; =&amp;gt; &#039; colspan=&amp;quot;3&amp;quot;&#039;); // specifying the colspan is optional but will help CustomFields know how to correctly print the table&lt;br /&gt;
$reshook=$hookmanager-&amp;gt;executeHooks(&#039;formObjectOptions&#039;,$parameters,$object,$action);    // Note that $action and $object may have been modified by hook&lt;br /&gt;
&lt;br /&gt;
print &#039;&amp;lt;/table&amp;gt;&#039;; // end of the table, this is what you&#039;re looking for, place the hook just above&lt;br /&gt;
&lt;br /&gt;
// ... a lot of php code ... redo the same if you find another &amp;lt;/table&amp;gt; and there&#039;s a page where there&#039;s no custom fields but you want to place custom fields on there&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note2: If after implementing the hook you receive an error or custom fields don&#039;t appear, you may have to change $object to another variable, depending on what is called the main object (eg: in Agenda it&#039;s called $action and $actioncomm).&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Lines hooks&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
For product lines, you should anyway use the product lines template files: htdocs/core/tpl/freeproductline_edit.tpl.php&lt;br /&gt;
&lt;br /&gt;
If you don&#039;t, you can still manually implement the required hooks, what is required is that you supply $line (which is the current line object being processed) in the $parameters array:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$parameters=array(&#039;line&#039;=&amp;gt;$line,&#039;fk_parent_line&#039;=&amp;gt;$line-&amp;gt;fk_parent_line);&lt;br /&gt;
&lt;br /&gt;
echo $hookmanager-&amp;gt;executeHooks(&#039;formEditProductOptions&#039;,$parameters,$this,$action);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hooks names:&lt;br /&gt;
* formEditProductOptions&lt;br /&gt;
* formCreateProductOptions ($line is not required here, because we create a new product line! you can leave $parameters=array(); empty).&lt;br /&gt;
Note: there&#039;s no hook for viewing, because showing the customfields will clutter the visual field, but if you really want you can also do it by adding a formViewProductOptions hook.&lt;br /&gt;
&lt;br /&gt;
=== Triggers ===&lt;br /&gt;
&lt;br /&gt;
First you need to know how to implement triggers in Dolibarr. For more informations about how to implement triggers in your module, please read the [[Triggers#Manage_and_create_a_new_trigger.27s_action|triggers wiki page]].&lt;br /&gt;
&lt;br /&gt;
An example case of implementation of hooks into a Dolibarr core file can be found in the [[Module_CustomFields_Cases|CustomFields Example Cases wiki page]].&lt;br /&gt;
&lt;br /&gt;
Implementing triggers on your module will allow CustomFields to save the fields at creation (datasheet edition is already managed by hooks).&lt;br /&gt;
&lt;br /&gt;
You need to implement 3 triggers:&lt;br /&gt;
- on creation of object - eg: YOURMODULE_CREATE&lt;br /&gt;
- on creation of lines - eg: YOURMODULELINE_INSERT (optional, only if your module has product lines)&lt;br /&gt;
- on update of lines - eg: YOURMODULELINE_UPDATE (optional, only if your module has product lines)&lt;br /&gt;
&lt;br /&gt;
Note: you need to supply the sql id of the newly created object before calling the trigger.&lt;br /&gt;
&lt;br /&gt;
Eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$this-&amp;gt;id = $this-&amp;gt;db-&amp;gt;last_insert_id(MAIN_DB_PREFIX.&amp;quot;yourmodule_table_element&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
// Appel des triggers&lt;br /&gt;
include_once(DOL_DOCUMENT_ROOT . &amp;quot;/core/class/interfaces.class.php&amp;quot;);&lt;br /&gt;
$interface=new Interfaces($this-&amp;gt;db);&lt;br /&gt;
$result=$interface-&amp;gt;run_triggers(&#039;YOURMODULE_CREATE&#039;,$this,$user,$langs,$conf);&lt;br /&gt;
if ($result &amp;lt; 0) {&lt;br /&gt;
    $error++; $this-&amp;gt;errors=$interface-&amp;gt;errors;&lt;br /&gt;
}&lt;br /&gt;
// Fin appel triggers&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note2: if it&#039;s a Dolibarr core module, you can easily find the right place to put the trigger by searching for &#039;create sql&#039; in the class subfolder (eg: URL is /htdocs/commande/fiche.php, the class is in /htdocs/commande/class/commande.class.php).&lt;br /&gt;
&lt;br /&gt;
Note3: CustomFields also supports a few other triggers like _CLONE, _UPDATE, _MODIFY, but they are not required and may not work depending on how your module works to manage these actions.&lt;br /&gt;
&lt;br /&gt;
== Configuring CustomFields to support the module ==&lt;br /&gt;
&lt;br /&gt;
=== Preliminary work ===&lt;br /&gt;
&lt;br /&gt;
You need to get the parameters of this module, and you will then put them in the CustomFields configuration file (conf_customfields.lib.php).&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Getting the parameters automatically&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
You can use the [[Module_CustomFields_Dev#Preliminary_test|preliminary test]] to easily get all those parameters.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Getting the parameters manually&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
To configure CustomFields to support a module, you need at least 5 parameters:&lt;br /&gt;
* the module&#039;s table_name (name of the sql table where the module stores its datas)&lt;br /&gt;
* the module&#039;s context (name given by the module to the hookmanager)&lt;br /&gt;
* the module&#039;s idvar (name of the variable that holds the sql row id of the record)&lt;br /&gt;
* the module&#039;s trigger action(s) (triggers on creation and update, see the related chapter above)&lt;br /&gt;
* the module&#039;s rights (for security purposes)&lt;br /&gt;
&lt;br /&gt;
To get these parameters manually, you can take a look inside the code of the module&#039;s files you want to implement:&lt;br /&gt;
* module&#039;s table name: either find it by yourself in the database (looks like llx_themodulename) or by printing it in actions_customfields.class.php by adding &#039;&#039;&#039;print_r($parameters)&#039;&#039;&#039; and search for $parameters-&amp;gt;table_element&lt;br /&gt;
* module&#039;s context: search for &amp;quot;callHooks(&amp;quot; without the quotes or take a look at the wiki: http://wiki.dolibarr.org/index.php/Hooks_system&lt;br /&gt;
or you can get it by printing it in the /htdocs/customfields/class/actions_customfields.class.php by adding print_r($parameters) and search for $parameters-&amp;gt;context&lt;br /&gt;
* module&#039;s trigger actions: search for &amp;quot;run_triggers(&amp;quot; or take a look at the wiki: http://wiki.dolibarr.org/index.php/Triggers#List_of_known_triggers_actions&lt;br /&gt;
* module&#039;s idvar: look at the url when you access one of the module&#039;s record, and you should see something like fiche.php?xxxid=12 - you need the xxxid (eg: socid, facid, etc.).&lt;br /&gt;
* module&#039;s security: in the page shown in URL when accessing the module&#039;s datasheet, edit this file and add &#039;&#039;&#039;print($user-&amp;gt;rights)&#039;&#039;&#039;, then in the list try to find the correct right (generally something like $user-&amp;gt;modulename-&amp;gt;creer). You can also try to search for $user-&amp;gt;rights in the sourcecode of this module.&lt;br /&gt;
&lt;br /&gt;
With these values, edit the config file (/htdocs/customfields/conf/conf_customfields.lib.php), particularly the $modulesarray and $triggersarray variables:&lt;br /&gt;
&lt;br /&gt;
=== Configure hooks and main parameters ===&lt;br /&gt;
&lt;br /&gt;
Add every parameters except triggers in the $modulesarray:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$modulesarray = array(&lt;br /&gt;
                      array(&#039;context&#039;=&amp;gt;&#039;invoicecard&#039;, &#039;table_element&#039;=&amp;gt;&#039;facture&#039;, &#039;idvar&#039;=&amp;gt;&#039;facid&#039;, &#039;rights&#039;=&amp;gt;array(&#039;$user-&amp;gt;rights-&amp;gt;facture-&amp;gt;creer&#039;)), // Client Invoice&lt;br /&gt;
                      array(&#039;context&#039;=&amp;gt;&#039;membercard&#039;, &#039;table_element&#039;=&amp;gt;&#039;adherent&#039;, &#039;idvar&#039;=&amp;gt;&#039;rowid&#039;, &#039;rights&#039;=&amp;gt;array(&#039;$user-&amp;gt;rights-&amp;gt;adherent-&amp;gt;creer&#039;), &#039;tabs&#039;=&amp;gt;array(&#039;objecttype&#039;=&amp;gt;&#039;member_admin&#039;, &#039;function&#039;=&amp;gt;&#039;member_admin_prepare_head&#039;, &#039;lib&#039;=&amp;gt;DOL_DOCUMENT_ROOT.&#039;/core/lib/member.lib.php&#039;)), // Members / Adherents&lt;br /&gt;
                     ); // Edit me to add the support of another module - NOTE: Lowercase only!&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are also a lot of others parameters you can give to CustomFields so that the module gets better managed or to add a few other functionnalities, here is a non-exhaustive list:&lt;br /&gt;
* context: name of the module&#039;s context.&lt;br /&gt;
* table_element: name of the sql table that holds the object&#039;s datas.&lt;br /&gt;
* idvar: name of the variable that holds the id of the object in the sql database (by default &#039;id&#039; or &#039;rowid&#039;).&lt;br /&gt;
* rights: prevent the custom fields to be editable unless all the rights set here are enabled (preventing custom fields to be viewed is useless because they won&#039;t show anyway if the page can&#039;t be loaded due to insufficient rights, so this is directly handled by the module).&lt;br /&gt;
* tabs=&amp;gt;array(objecttype, function, lib, tabname, tabtitle): used to embed CustomFields&#039;s admin panel into another module&#039;s admin page.&lt;br /&gt;
&lt;br /&gt;
=== Configure creation triggers ===&lt;br /&gt;
&lt;br /&gt;
Add the triggers in $triggersarray:&lt;br /&gt;
$triggersarray = array(&amp;quot;order_create&amp;quot;=&amp;gt;&amp;quot;commande&amp;quot;,&lt;br /&gt;
                       &amp;quot;yourmodule_triggeraction1&amp;quot;=&amp;gt;&amp;quot;yourmodule_tablename&amp;quot;,&lt;br /&gt;
                       &amp;quot;yourmodule_triggeraction2&amp;quot;=&amp;gt;&amp;quot;yourmodule_tablename&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
Note: generally you will be looking to implement the _CREATE.&lt;br /&gt;
&lt;br /&gt;
Note2: if a triggeraction is not recognized by CustomFields (eg: mymodule_addpayment) then by default CustomFields will assimilate it to a _create action (it will create and save the values for the custom fields, just for a module&#039;s creation page).&lt;br /&gt;
&lt;br /&gt;
Note3: CustomFields also supports a few other triggers like _CLONE, _UPDATE, _MODIFY, but they are not required and may not work depending on how your module works to manage these actions.&lt;br /&gt;
&lt;br /&gt;
=== Configuration troubleshooting ===&lt;br /&gt;
&lt;br /&gt;
* If you cannot see the custom fields appear on your module&#039;s page, this means the hooks are not implemented or configured in CustomFields, please read the related chapters.&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet but there&#039;s no edit button icon: the module implements security but you did not configure it properly in CustomFields, please find the right security in $user-&amp;gt;rights and set it in conf_customfields.lib.php.&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the creation page but are not saved: you forgot to set the trigger in $triggersarray, or the creation trigger does not exist for this module and you have to implement it. Jump to [[Module_CustomFields_Dev#Triggers]].&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet but not on the creation page (or vice versa): A hook is missing on the page where the custom fields do not appear. Jump to [[Module_CustomFields_Dev#Hooks]].&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet with the edit button, but when the edit button is clicked, everything disappear: either the $idvar is not properly set (look at the URL to know if you need to set idvar), or the hook is not properly made ($object is not defined and it&#039;s called by another name, but the hook use $object instead of the correct variable).&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet with the edit button, can be edited but cannot be saved (the data is not saved): same as the previous point: either $idvar or $object bug.&lt;br /&gt;
&lt;br /&gt;
Also you might find useful to &#039;&#039;&#039;[[Module_CustomFields#General_advices_for_troubleshooting|enable php errors]]&#039;&#039;&#039; to better track errors if that&#039;s not already done.&lt;br /&gt;
&lt;br /&gt;
= Example cases =&lt;br /&gt;
&lt;br /&gt;
You can find a compilation of concrete, practical examples on the [[Module_CustomFields_Cases]] wiki page.&lt;br /&gt;
&lt;br /&gt;
= TO DO =&lt;br /&gt;
&lt;br /&gt;
You will find below the public list of features that should be implemented in the future.&lt;br /&gt;
&lt;br /&gt;
If you have an idea about a new feature that isn&#039;t listed below, feel free to add it to the list!&lt;br /&gt;
&lt;br /&gt;
== To Do ==&lt;br /&gt;
Nothing here!&lt;br /&gt;
&lt;br /&gt;
== To Document ==&lt;br /&gt;
* Automatic recopy on cloning for custom fields and products&#039; lines custom fields.&lt;br /&gt;
* new API function: customfields_clone_or_recopy($object, $fromobject, $action2 = null)&lt;br /&gt;
&lt;br /&gt;
== Should do ==&lt;br /&gt;
* Add the possibility to mark a custom field to be shown in tables listings (eg: as a column in products listings) with &amp;quot;order by this column&amp;quot; buttons and stuffs. Will be difficult if the hooks are not yet implemented in listings.&lt;br /&gt;
* Checkbox (multiple checkboxes, one for each possible value) and multiselect (big select and can use SHIFT to multiselect) (store in XML file) (but impossible with constrained fields, can&#039;t store multiple foreign keys ids in one single sql field, but would work for other types like enum)&lt;br /&gt;
use fieldset, input checkbox and label for, eg:&lt;br /&gt;
    &amp;lt;fieldset&amp;gt;&amp;lt;legend&amp;gt;&amp;lt;label for=&amp;quot;cf_productslist&amp;quot;&amp;gt; $langs-&amp;gt;trans(&#039;cf_productslist&#039;)&amp;lt;/label&amp;gt;&amp;lt;/legend&amp;gt;&lt;br /&gt;
    &amp;lt;input type=&amp;quot;checkbox&amp;quot; name=&amp;quot;cf_productslist_choice1&amp;quot; value=&amp;quot;1&amp;quot; id=&amp;quot;cf_productslist_choice1&amp;quot; /&amp;gt;&amp;lt;label for=&amp;quot;cf_productslist_choice1&amp;quot;&amp;gt; $langs-&amp;gt;trans(&#039;cf_productslist_choice1&#039;)&amp;lt;/label&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
    &amp;lt;input type=&amp;quot;checkbox&amp;quot; name=&amp;quot;cf_productslist_choice2&amp;quot; value=&amp;quot;1&amp;quot; id=&amp;quot;cf_productslist_choice2&amp;quot; /&amp;gt;&amp;lt;label for=&amp;quot;cf_productslist_choice2&amp;quot;&amp;gt; $langs-&amp;gt;trans(&#039;cf_productslist_choice2&#039;)&amp;lt;/label&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
    etc...&lt;br /&gt;
    &amp;lt;/fieldset&amp;gt;&lt;br /&gt;
* Add a javascript options values generator for the enum type (a hidden input that would be shown only when DropdownBox type is selected and that would permit to add options by clicking a plus button).&lt;br /&gt;
* Replace triggers in customfields class by hookmanager?&lt;br /&gt;
* AJAX previsualisation of the HTML custom field that will be created.&lt;br /&gt;
* In ODTs, create special tags to directly access day, month or year value of a date type field independently of the rest.&lt;br /&gt;
* New overloading function&#039;s action: &amp;quot;get&amp;quot;, similar to view except that it works not only on Dolibarr&#039;s view but whenever someone tries to access this field&#039;s value.&lt;br /&gt;
* Implementation of custom fields in CSV exports (may need a few new hooks in Dolibarr?).&lt;br /&gt;
* Add Upload and Image field types (this would allow for an easier integration inside ODT and PDF templates than the current workflow of Dolibarr).&lt;br /&gt;
&lt;br /&gt;
== Known bugs ==&lt;br /&gt;
* in product and service modules, if you edit a field, the proposals and other fields below won&#039;t be shown, you need to refresh the page. This problem resides in Dolibarr I think (since we are simply using a hook).&lt;br /&gt;
* ficheinter (Intervention module) custom fields will stay in create mode just after create page is submitted (this is because of the $action that is not standard - $action=edit or something).&lt;br /&gt;
&lt;br /&gt;
== Never/Maybe one day ==&lt;br /&gt;
* Add support for repeatable (predefined) invoices (the way it is currently managed makes it very difficult to manage this without making a big exception, adding specific functions in customfields modules that would not at all will be reusable anywhere else, when customfields has been designed to be as generic as possible to support any module and any version of dolibarr, because it&#039;s managed by a totally different table while it&#039;s still managed by the same module, CustomFields work with the paradigm: one module, one table).&lt;br /&gt;
* Add an AJAX select box for constrained values : when a constrained type is selected and a table is selected, a hidden select box would show up with the list of the fields of this table to choose the values that will be printed as the values for this customfield (eg: for table llx_users you could select the &amp;quot;nom&amp;quot; field and then it would automatically prepend &amp;quot;nom_&amp;quot; to the field&#039;s name).&lt;br /&gt;
* Fine-grained rights management: rights per group, per user, and per field.&lt;br /&gt;
* Replace overloading functions (extend class) by hookmanager if possible (but how to simulate the *full switch? Plus this will force users to make their own modules to make those functions, and to disable/renable their modules everytime they will add a new function...).&lt;br /&gt;
* Refactor the trigger array: merge it with the modulesarray (along with a new way to specify which customfields trigger action a trigger should correspond, eg: &#039;linebill_insert&#039;=&amp;gt;&#039;customfields_create&#039;). But the triggers aren&#039;t necessarily associated with a specific module...&lt;/div&gt;</summary>
		<author><name>Lrq3000</name></author>
	</entry>
	<entry>
		<id>https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Dev&amp;diff=33119</id>
		<title>Module CustomFields Dev</title>
		<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Dev&amp;diff=33119"/>
		<updated>2015-01-22T21:41:14Z</updated>

		<summary type="html">&lt;p&gt;Lrq3000: /* Fetching referenced records of a constrained field */ Adding section /* Recursive fetching referenced records of a constrained field */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:CustomFields]]&lt;br /&gt;
{{TemplateDocDevEn}}&lt;br /&gt;
{{TemplateModEN}}&lt;br /&gt;
{{BasculeDevUserEn|&lt;br /&gt;
name=CustomFields|&lt;br /&gt;
num=8500|&lt;br /&gt;
devdoc=This page|&lt;br /&gt;
userdoc=[[Module_CustomFields]]|}}&lt;br /&gt;
[[Category:FAQ EN]]&lt;br /&gt;
&lt;br /&gt;
These notes are aimed to developpers who want to extend the functionnalities of the module or want to use it for extreme cases.&lt;br /&gt;
This is not meant to guide implementers on how to implement and configure the module (see the related chapter for that).&lt;br /&gt;
&lt;br /&gt;
If you are an implementer and just want to use CustomFields with its normal functions, you can read the [[Module_CustomFields|user documentation of CustomFields]].&lt;br /&gt;
&lt;br /&gt;
You can also jump directly to [[Module_CustomFields_Cases]] wiki page for a collection of practical examples.&lt;br /&gt;
&lt;br /&gt;
= Principles =&lt;br /&gt;
&lt;br /&gt;
CustomFields is simply a wrapper for SQL databases.&lt;br /&gt;
&lt;br /&gt;
The module is based on SQL to manage the fields and most of the stuff. It uses and understands standard SQL.&lt;br /&gt;
&lt;br /&gt;
The main principles of CustomFields are:&lt;br /&gt;
&lt;br /&gt;
* one SQL column = one Dolibarr field.&lt;br /&gt;
* one SQL type = one method to show and edit the field in Dolibarr.&lt;br /&gt;
* almost everything should be configurable from the configuration file, or admin interface.&lt;br /&gt;
* be independent from Dolibarr&#039;s data structures.&lt;br /&gt;
&lt;br /&gt;
CustomFields sits besides Dolibarr, and does not mix with it, even if in the Dolibarr&#039;s interface it looks like CustomFields is embedded inside Dolibarr, in reality, CustomFields is totally independent.&lt;br /&gt;
&lt;br /&gt;
= Goals =&lt;br /&gt;
&lt;br /&gt;
This module was made to provide an easy way to manage custom sql fields, not only for users and implementers, but also for developpers to use in their own module:&lt;br /&gt;
* by easily adding the support of custom fields for your own module by using this module;&lt;br /&gt;
* by reusing custom fields from another module in your own module;&lt;br /&gt;
* by managing sql fields in your own module by using the CustomFields class (which will take care of everything from printing, editing to PDF/ODT templating).&lt;br /&gt;
&lt;br /&gt;
So this module is not only an auxiliary of other modules, but it can also be the core part of your own module by using it as your SQL query class for fields.&lt;br /&gt;
&lt;br /&gt;
= SQL structure =&lt;br /&gt;
&lt;br /&gt;
A CustomFields table needs to respect three parameters that never change:&lt;br /&gt;
&lt;br /&gt;
* table&#039;s name (format: llx_*dolibarrmodule*_customfields where *dolibarrmodule* = table_element in conf_customfields.lib.php)&lt;br /&gt;
* first column: rowid, which is the primary key for each field&lt;br /&gt;
* second column: fk_*dolibarrmodule* which is the foreign key that links every customfields record to the Dolibarr&#039;s module that is associated (invoice, order, etc...).&lt;br /&gt;
&lt;br /&gt;
Apart from the table&#039;s name and the first two special columns, every other columns are just standard sql columns that you can freely create, edit or delete as you want, from the module&#039;s administration page or from a third-party SQL management tool (such as phpMyAdmin).&lt;br /&gt;
&lt;br /&gt;
You can also create more complex sql fields: constraints, foreign keys, transformations, other sql types, etc...).&lt;br /&gt;
&lt;br /&gt;
Then the module will try to manage the sql fields you manually created the best it can:&lt;br /&gt;
* by printing appropriate input fields in the Dolibarr gui interface.&lt;br /&gt;
* by providing the developper an easy access to these fields in php (eg: $customfields-&amp;gt;cf_myfield or $object-&amp;gt;customfields-&amp;gt;cf_myfield) for any use, including PDF templates and ODT templates, or in any php script (so that you can use these fields in your own module).&lt;br /&gt;
&lt;br /&gt;
= SQL Indexes =&lt;br /&gt;
&lt;br /&gt;
The module automatically adds an index on the primary key and foreign key (first and second columns).&lt;br /&gt;
&lt;br /&gt;
To further optimize the performances of your database, and particularly the customfields tables, you may want to manually add indexes based on usage.&lt;br /&gt;
&lt;br /&gt;
Also, you may add indexes on remote tables used by constrained fields, which could optimize even more the performances since constrained fields have the worst performance drawback for CustomFields.&lt;br /&gt;
&lt;br /&gt;
= SQL Compatibility mode =&lt;br /&gt;
&lt;br /&gt;
The module relies on referential integrity checks and foreign keys of your DBMS to keep the database clean and to provide the constrained fields feature. Referential integrity checks is part of the SQL standards and should be used whenever possible, which always was a main goal for CustomFields (to be respectful of SQL standards).&lt;br /&gt;
&lt;br /&gt;
Unluckily, a few DBMS systems still don&#039;t support this critical functionality of the SQL standard, for example MySQL enables MyISAM engine by default, which doesn&#039;t support this feature.&lt;br /&gt;
&lt;br /&gt;
In these cases, you can either do two things:&lt;br /&gt;
* Switch to an engine which support referential integrity. For example with MySQL, you can enable InnoDB, which support referential integrity checks and foreign keys.&lt;br /&gt;
* Use CustomFields compatibility mode (more infos below).&lt;br /&gt;
&lt;br /&gt;
In the case your database does not support referential integrity checks, CustomFields will automatically switch to the SQL compatibility mode, which will allow you to use CustomFields even on databases where foreign keys are not possible.&lt;br /&gt;
&lt;br /&gt;
In compatibility mode, you will get nearly all CustomFields advanced features such as Constrained fields, but the customfields&#039; tables in the database won&#039;t be cleaned automatically (no delete on cascade, meaning that when you delete an object, the linked custom fields will still be kept in your database), and constrained fields won&#039;t be updated automatically on update.&lt;br /&gt;
&lt;br /&gt;
Nevertheless, these missing cleanup functionalities won&#039;t be seen by your users because they will never be accessible: on deletion of an object, the custom fields won&#039;t ever be accessible again from Dolibarr&#039;s interface, and object&#039;s ids are never changed since Dolibarr also relies on this. Thus in compatibility mode, CustomFields will act just like normally, but your database will be a bit less clean.&lt;br /&gt;
&lt;br /&gt;
= JSON extension for extra options =&lt;br /&gt;
&lt;br /&gt;
== Introduction to JSON in CustomFields ==&lt;br /&gt;
The relational model is very great to store and fetch data with a declarative language, and this is very powerful, but this model also imposes a few limitations, for example the relations must be well formatted and predefined.&lt;br /&gt;
&lt;br /&gt;
That&#039;s why we finally implemented a &#039;&#039;&#039;JSON extension&#039;&#039;&#039; to manage &#039;&#039;&#039;extra options&#039;&#039;&#039; and associate them with custom fields. This allows to add many properties (or columns if you are more used to the relational model) on-the-fly without having to predefine them prior to storing and using them.&lt;br /&gt;
&lt;br /&gt;
This allows to add an undefined number of columns/properties/extra options that will help manage special behaviours for custom fields. For example, it is currently used to allow to change the order of appearance of custom fields, and to store and manage remote table and columns datas for constrained fields on DBMS that do not support Foreign Keys checks.&lt;br /&gt;
&lt;br /&gt;
We chose JSON over XML because the library are simpler and the data footprint smaller, and because CustomFields does not need advanced features of XML like XQuery, data typing and meta structures (at least not for now).&lt;br /&gt;
&lt;br /&gt;
The other advantage is that php objects can directly be encoded into JSON without any preprocessing, and can then as straightly be decoded from JSON back into a php object.&lt;br /&gt;
&lt;br /&gt;
A simple mean to encode and decode back an object into and from a storage place, as well as allow users to easily add/remove extra options were the main requirement, and JSON (+ JSON libraries for PHP) meet this criteria.&lt;br /&gt;
&lt;br /&gt;
However, note that the extra options are now encoded as an associative array, so as to avoid any ambiguities (there&#039;s no associative array in JSON, they are converted to objects).&lt;br /&gt;
&lt;br /&gt;
== Implementation of JSON ==&lt;br /&gt;
The implementation is very simple: a table called &#039;&#039;&#039;llx_customfields_extraoptions&#039;&#039;&#039; is created when you enable the CustomFields module, and this table only contains three columns:&lt;br /&gt;
* table_name (varchar)&lt;br /&gt;
* column_name (varchar)&lt;br /&gt;
* extraoptions (blob)&lt;br /&gt;
&lt;br /&gt;
(table_name, column_name) both forms the primary key of the table, and links to columns (custom fields) of other customfields tables.&lt;br /&gt;
extraoptions is a blob, which allows to store the data as binary (as-is) without encoding (which solves a big problem of JSON which is encoding special and foreign characters, here there is no problem at all).&lt;br /&gt;
&lt;br /&gt;
When fetching custom fields, the module simply JOIN the extraoptions table, which adds an &#039;&#039;&#039;extraoptions&#039;&#039;&#039; column for each custom field in the SQL result. Then it is quite easy to just json_decode() the data returned inside the extraoptions column.&lt;br /&gt;
&lt;br /&gt;
This methodology allows to have all the advantages of a relational database, with the flexibility of a free-schema model:&lt;br /&gt;
* everything is stored inside the database (only one storage place)&lt;br /&gt;
* fixed columns allows for relational joining and optimization&lt;br /&gt;
* thus queries (fetching) are optimised&lt;br /&gt;
* at the same time, the extraoptions are automatically retrieved and associated with every custom field, and whenever needed they can be used&lt;br /&gt;
* and thus we also get a flexible model to &#039;&#039;&#039;add extra (virtual) columns whenever needed&#039;&#039;&#039; without having to alter the relational database schema&lt;br /&gt;
&lt;br /&gt;
Thus no critical information is and should be stored inside the extraoptions column because they would not be query-able, only extra options that are just used to tweak the management of those fields inside the PHP application should be stored. But this is a wonderful way to add non critical informations that still allows to develop more complex features and management for special custom fields.&lt;br /&gt;
&lt;br /&gt;
== CustomFields JSON API ==&lt;br /&gt;
&lt;br /&gt;
=== Storing extra options ===&lt;br /&gt;
&lt;br /&gt;
You can use the &#039;&#039;&#039;setExtra()&#039;&#039;&#039; method to set extra options of a custom field.&lt;br /&gt;
&lt;br /&gt;
Eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$extra = array(); // $extra should always be an associative array, it&#039;s no longer an object&lt;br /&gt;
$extra[&#039;myproperty&#039;] = &#039;Anything I want here&#039;; // set a property/extra option like this&lt;br /&gt;
$extra[&#039;category&#039;][&#039;subcategory&#039;][&#039;prop&#039;] = true; // we can set recursive properties, there&#039;s not any limit to how deep the hierarchy can be&lt;br /&gt;
&lt;br /&gt;
require_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomField($db, &#039;currentmodule&#039;);&lt;br /&gt;
$fieldid = &#039;mycustomfields&#039;; // $fieldid can either be a column_name or an ordinal_position from information_schema.columns&lt;br /&gt;
$result = $customfields-&amp;gt;setExtra($fieldid, $extra); // store the extra options inside the database&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can also use addCustomField() or updateCustomField() with an $extra array, and these functions will also call the setExtra() method.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: the extra options you set are &#039;&#039;&#039;appended&#039;&#039;&#039; to the ones already stored in the database. However, you can also overwrite previous options, just use the same name of property but with a different value (the replacement is recursive, you can do it at any level in the hierarchy).&lt;br /&gt;
&lt;br /&gt;
=== Fetching extra options ===&lt;br /&gt;
&lt;br /&gt;
Extra options are automatically fetched along with a custom field structure, decoded, and stored inside the extra sub-property of any field object, or CustomFields object.&lt;br /&gt;
&lt;br /&gt;
When fetched, extra options are stored as a sub-object for the current field object (which itself is stored as a subobject of the CustomFields object if caching is enabled).&lt;br /&gt;
&lt;br /&gt;
Eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
require_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomField($db, &#039;currentmodule&#039;);&lt;br /&gt;
$fields = fetchAllFieldsStruct(); // fetching fields structure will automatically fetch extra options at the same time. This works with fetchAllFieldsStruct() and fetchFieldStruct()&lt;br /&gt;
&lt;br /&gt;
// Now that you fetched the fields structure, you can just access the extra options sub-properties:&lt;br /&gt;
print_r($fields-&amp;gt;mycustomfield-&amp;gt;extra); // will print all extra options for the field mycustomfield&lt;br /&gt;
print_r($customfields-&amp;gt;fields-&amp;gt;mycustomfield-&amp;gt;extra); // you can also access the same field(s) directly from the CustomFields object if caching is enabled (default behaviour)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Modifying extra options ===&lt;br /&gt;
&lt;br /&gt;
You can just combine the two previous points to edit extra options of a custom field: just load a custom field, edit on-the-fly the extra property with PHP code, and then store it back directly into the database:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
require_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomField($db, &#039;currentmodule&#039;);&lt;br /&gt;
$fields = fetchAllFieldsStruct();&lt;br /&gt;
&lt;br /&gt;
$fields-&amp;gt;mycustomfield-&amp;gt;extra[&#039;newproperty&#039;] = &#039;This is a new property&#039;; // add a new property over the old extra options sub-object&lt;br /&gt;
$fields-&amp;gt;mycustomfield-&amp;gt;extra[&#039;mycategory&#039;] = &#039;Overwrite the old category&#039;; // this will replace the previous value of &#039;mycategory&#039; extra option&lt;br /&gt;
&lt;br /&gt;
$customfields-&amp;gt;setExtra(&#039;mycustomfield&#039;, $fields-&amp;gt;mycustomfield-&amp;gt;extra); // store the modified extra options back into the database for future usage&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= CustomFields API =&lt;br /&gt;
&lt;br /&gt;
== Introduction to CustomFields API ==&lt;br /&gt;
&lt;br /&gt;
This chapter will cover the basics on how to manually and concretely use the CustomFields class to create/update/fetch/manage custom fields.&lt;br /&gt;
&lt;br /&gt;
This will give you a better understanding of how CustomFields works, as well as giving you insights on how to use it for your own applications.&lt;br /&gt;
&lt;br /&gt;
Important note: contrary to the Facade API, the core CustomFields class use columns names without any prefix, so don&#039;t try to use the prefix, it won&#039;t work! For example: if your field is named &#039;mycustomfield&#039; and prefix is &#039;cf_&#039;, when you use the Facade API you should use &#039;$object-&amp;gt;$path-&amp;gt;cf_mycustomfield&#039; but when you use the core CustomFields class directly, $fieldname = &#039;mycustomfield&#039;.&lt;br /&gt;
&lt;br /&gt;
== Prerequisite to use the CustomFields class ==&lt;br /&gt;
&lt;br /&gt;
Before using the CustomFields class, you first must instanciate it. This is a necessary preliminary step before using any of the methods that will be covered inside this chapter, and thus will not be repeated, only shown once here.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Include the CustomFields class (which contains the main API)&lt;br /&gt;
require_once(dirname(__FILE__).&#039;/../class/customfields.class.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Instanciate a CustomFields object&lt;br /&gt;
global $db; // the database handler created by Dolibarr&lt;br /&gt;
$table_element = &#039;currentmodule&#039;; // where $table_element is the database&#039;s table name for the module, without the Dolibarr prefix (eg: for &#039;llx_facture&#039;, table_element is &#039;facture&#039;). You can also find it inside the $object properties (just print_r() it).&lt;br /&gt;
$customfields = new CustomFields($db, $table_element); // instanciate the CustomFields class inside $customfields object&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
CustomFields keeps track of errors that can happen when executing methods and SQL queries inside the &#039;&#039;&#039;error&#039;&#039;&#039; and &#039;&#039;&#039;errors&#039;&#039;&#039; properties, which you can print with the printErrors() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Do something with CustomFields before&lt;br /&gt;
&lt;br /&gt;
if (count($customfields-&amp;gt;errors) &amp;gt; 0) { // If there is at least one error&lt;br /&gt;
    $customfields-&amp;gt;printErrors(); // print errors&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// You can also print your own error message, in two manners:&lt;br /&gt;
&lt;br /&gt;
// 1- submit your own message&lt;br /&gt;
$customfields-&amp;gt;printErrors(&#039;Your own error message here&#039;); // this will print ONLY your error message, using the Dolibarr&#039;s AJAX error dialog (standard way of printing errors)&lt;br /&gt;
&lt;br /&gt;
// 2- append your message in the customfields errors&lt;br /&gt;
$customfields-&amp;gt;errors[] = &#039;Your own error message to be appended with other CustomFields messages&#039;;&lt;br /&gt;
$customfields-&amp;gt;printErrors(); // Here your error message will be appended, thus all error messages will be printed. This allows you for example to add more intelligible informations on a specific error.&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== General purpose SQL methods ==&lt;br /&gt;
&lt;br /&gt;
CustomFields provide an interface to easily execute various common SQL requests, as well as your own custom SQL requests.&lt;br /&gt;
&lt;br /&gt;
* fetchAllTables() fetch a list of all the tables in the database&lt;br /&gt;
* fetchPrimaryField($table) fetch the column_name (and only the column_name!) of the primary field of a table. You can then use fetchFieldStruct()  with the column_name to get any information you want.&lt;br /&gt;
* fetchAny($columns, $table, $where=&#039;&#039;, $orderby=&#039;&#039;, $limitby=&#039;&#039;) allows you to issue any simple SQL query command, for example fetchAny(&#039;*&#039;, &#039;sometable&#039;) is equivalent to SELECT * FROM sometable; (but then everything is managed automatically and result is returned to you)&lt;br /&gt;
* executeSQL($sql, $eventname) to execute any SQL query you want, this will return you a resource (and NOT a processed nuplet like fetchAny!). $eventname can be anything, it&#039;s just for Dolibarr logging facility.&lt;br /&gt;
* executeMultiSQL($sql, $eventname) to execute multiple SQL queries&lt;br /&gt;
* fetchFieldStruct($id=null, $nohide=false, $table=null, $cachepath=null) allows you to fetch the field&#039;s SQL structure of not only custom fields, but of any database&#039;s column using the $table variable. Eg: fetchFieldStruct(&#039;mycolumn&#039;, true, &#039;mytable&#039;);&lt;br /&gt;
* probeTable($table) helps you to check if a table exists in the database&lt;br /&gt;
* fetch_object($resource) will return a PHP object from a $resource, similarly to mysql_fetch_object() but with two advantages: it works on several DBMS (not only mysql), and it always return lowercase properties names (else depending on the configuration of the DBMS, the request can return uppercase or mixed case properties names).&lt;br /&gt;
* escape() and reverse_escape() are similare to mysql_real_escape but is reversible and DBMS-independent (so you can use it with other DBMSes).&lt;br /&gt;
&lt;br /&gt;
== Managing CustomFields structure ==&lt;br /&gt;
&lt;br /&gt;
The first step into CustomFields is to create and manage the structure of a custom field.&lt;br /&gt;
&lt;br /&gt;
=== Creating a custom field definition ===&lt;br /&gt;
&lt;br /&gt;
To create a custom field, you can use the addCustomField() method.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;addCustomField($fieldname, $type, $size, $nulloption, $defaultvalue = null, $constraint = null, $customtype = null, $customdef = null, $customsql = null, $fieldid = null, $extra = null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will take care of forging the (complicated) SQL query with the forgeSQLCustomField() method, and will return you an error if there was a problem.&lt;br /&gt;
&lt;br /&gt;
=== Updating a custom field definition ===&lt;br /&gt;
&lt;br /&gt;
To update a custom field, you can use the updateCustomField() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;&lt;br /&gt;
updateCustomField($fieldid, $fieldname, $type, $size, $nulloption, $defaultvalue, $constraint = null, $customtype = null, $customdef = null, $customsql = null, $extra = null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: the updateCustomField() and addCustomField() methods can both be used since updateCustomField() is in fact an alias for addCustomField(). CustomFields will automatically detect if a custom field already exists, and update it if that&#039;s true, or create it.&lt;br /&gt;
&lt;br /&gt;
Note2: updating a custom field will automatically take care of the constraints and foreign keys on its own, thus you can change the foreign key, or remove it altogether if set to null.&lt;br /&gt;
&lt;br /&gt;
=== Delete a custom field definition ===&lt;br /&gt;
&lt;br /&gt;
You can easily delete a custom field by column_name or by ordinal_position:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;deleteCustomField($id);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: constraints and foreign keys will also be deleted automatically so you shouldn&#039;t encounter any error.&lt;br /&gt;
&lt;br /&gt;
=== Fetch one custom field&#039;s SQL structure ===&lt;br /&gt;
&lt;br /&gt;
To get the SQL structure of a custom field, which can be quite useful to know for example its datatype or if it has a foreign key, or accepts null values, you can use the fetchFieldStruct() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
fields = $customfields-&amp;gt;fetchFieldStruct($id=null, $nohide=false, $table=null, $cachepath=null); // where $id can be a column_name, an ordinal_position from information_schema.columns or null if you want to fetch all the columns at once&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method will either return null, either a single field object, either an array on field objects.&lt;br /&gt;
&lt;br /&gt;
Note: this function will use caching by default to accelerate future results, but you can disable it, or even change the default cache path. For more informations please read the comments in the source code.&lt;br /&gt;
&lt;br /&gt;
=== Fetch several custom fields&#039; SQL structures ===&lt;br /&gt;
&lt;br /&gt;
Use the fetchAllFieldsStruct() method, which is an alias for fetchFieldStruct():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;fetchAllFieldsStruct($nohide=false, $table=null, $cachepath=null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: this method will always return an array of fields objects, even if only one is found (for more consistency), and this is the main difference with fetchFieldStruct(null).&lt;br /&gt;
&lt;br /&gt;
== Managing custom fields records ==&lt;br /&gt;
&lt;br /&gt;
Now that you know how to create and manage the structure of custom fields, we can proceed on to managing the records.&lt;br /&gt;
&lt;br /&gt;
=== Creating a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Simply use the create() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;create($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
$object must be an object containing as its properties the customfields data.&lt;br /&gt;
&lt;br /&gt;
For example, if you have a field named mycustomfield, your object must have $object-&amp;gt;mycustomfields defined.&lt;br /&gt;
&lt;br /&gt;
This method will store in the database ALL the custom fields that were created/edited and with a valid value (eg: an empty value for a field that does not accept null value is invalid) AT ONCE! So you can modify all the columns of one record at once, but not several records at once.&lt;br /&gt;
&lt;br /&gt;
Concretely, this is very useful with Dolibarr, because modules tends to store everything inside a single object generally called $object, and CustomFields tries to hook onto $object, so that you can just pass $object as is to create() and it will save all the custom fields.&lt;br /&gt;
&lt;br /&gt;
But you can also create an empty object with $object = new stdClass(); and then manually fill the custom fields records with whatever you want, and then call the create() method, this will work as well!&lt;br /&gt;
&lt;br /&gt;
You can also just modify only one column of one record, by just specifying one custom field column_name in your $object. The other custom fields that are not specified won&#039;t be created/modified.&lt;br /&gt;
&lt;br /&gt;
=== Updating a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Use the update() method, which is an alias for the create() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;update($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As noted above, update() is an alias of the create() method, which means that CustomFields will try to detect by itself if a record needs to be updated or created.&lt;br /&gt;
&lt;br /&gt;
=== Deleting a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Use the delete() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;delete($id); // where $id is = to fk_moduleid column, NOT rowid&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fetching a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Just use the fetch() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$records = $customfields-&amp;gt;fetch($id=null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where $id can either be = fk_moduleid (NOT rowid), or null to fetch all the records at once.&lt;br /&gt;
&lt;br /&gt;
This method will either return null, a single record object, or an array of records objects.&lt;br /&gt;
&lt;br /&gt;
=== Fetching all custom fields&#039; records ===&lt;br /&gt;
&lt;br /&gt;
Use the fetchAll() method, which is an alias for fetch():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$records = $customfields-&amp;gt;fetchAll();&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: this method will always return an array of records object, for more consistency (and this is the main difference with fetch(null) ).&lt;br /&gt;
&lt;br /&gt;
=== Fetching referenced records of a constrained field ===&lt;br /&gt;
&lt;br /&gt;
A constrained custom field points to another database table, and a value for this constrained field is the rowid of a record in the referenced table.&lt;br /&gt;
&lt;br /&gt;
Thus, the principal purpose of a constrained custom field is not the value of the field in itself (it&#039;s simply an integer), but rather the other fields available from the referenced table, the constrained field being in fact just a pointer.&lt;br /&gt;
&lt;br /&gt;
To fetch the remote record from the referenced table, use the fetchReferencedValuesList() of the CustomFields class:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$fkrecord = fetchReferencedValuesList($field, $id=null, $where=null, $allcolumns=false);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
&lt;br /&gt;
* $field is a custom field object (fetched using fetchFieldStruct() or fetchAllFieldsStruct()),&lt;br /&gt;
* $id is the rowid of the record to get, just like fetch() and fetchAll(). Can be null to fetch all remote records (or to use the $where clause).&lt;br /&gt;
* $where is a string without the WHERE clause (eg: &amp;quot;fk_facture=1&amp;quot;) to get finer control. In this case, you can use $id or set $id=null depending on your purpose. This is mainly a private argument to allow for cascade option.&lt;br /&gt;
* $allcolumns if false, it will fetch only the required columns to do the smart value substitution. If true, it will fetch all remote columns (useful for ODT substitution). If an array of strings, each string will be a column to fetch from the remote database.&lt;br /&gt;
&lt;br /&gt;
Note that this function is NOT recursive (it will fetch the records from the referenced table, and that&#039;s all, it won&#039;t fetch the custom fields of the referenced table nor recursively fetch the remote constrained custom fields). To do recursive fetching, see in the Facade API.&lt;br /&gt;
&lt;br /&gt;
Note2: this function automatically manages Smart Value Substitution depending of the $field-&amp;gt;column_name.&lt;br /&gt;
&lt;br /&gt;
=== Recursive fetching referenced records of a constrained field ===&lt;br /&gt;
&lt;br /&gt;
If you want to recursively fetch a constrained field (eg: constrainedfield1-&amp;gt;ref_table-&amp;gt;constrainedfield2-&amp;gt;ref_table2-&amp;gt;...), you need another function called fetchReferencedValuesRec(). Technically, this function is in the Facade API (inside /customfields/lib/customfields_aux.lib.php), but it isn&#039;t meant to be used by lambda users (contrary to customfields_fill_object()), but is mainly a helper function for the rest of the facade API.&lt;br /&gt;
&lt;br /&gt;
fetchReferencedValuesRec() works very similarly to fetchReferencedValuesList(), but it will work recursively.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$fkrecord = fetchReferencedValuesRec($customfields, $field, $id, $recursive=true)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where $customfields is the CustomFields instance for the module you are working on, $field is the constrained field you want to fetch, $id is the record&#039;s id you want to fetch, and $recursive enables or disables the recursion.&lt;br /&gt;
&lt;br /&gt;
Note that this function automatically manages Smart Value Substitution (and also recursively).&lt;br /&gt;
&lt;br /&gt;
Note2: This function tries to automatically avoid an infinite recursion loop by using a $blacklist: for a given constrained custom field, a table won&#039;t be visited twice. This means that for example you can do: invoice-&amp;gt;user-&amp;gt;socpeople-&amp;gt;invoice, this will work and every referenced fields will be fetched, but it will automatically stop the recursion at the second invoice, because else it could continue in a loop to user, socpeople, invoice, user, socpeople, etc.&lt;br /&gt;
&lt;br /&gt;
== Input forms for custom fields ==&lt;br /&gt;
&lt;br /&gt;
The API provides some neat ways to automatically manage the input forms for users to input new values, with the best adapted HTML form based on the custom field&#039;s data type.&lt;br /&gt;
&lt;br /&gt;
The following methods will print input fields that are best adapted to the SQL data type of your custom fields as well as figure out the possible values and even format them (for example: a not null enum will be printed as a HTML select list without an empty option; fetch automatically remote columns for constrained fields and show them in a select).&lt;br /&gt;
&lt;br /&gt;
So you don&#039;t have to bother how your data will be presented to the user nor how it will be processed.&lt;br /&gt;
&lt;br /&gt;
=== Printing an input form adapted to the field&#039;s data type ===&lt;br /&gt;
&lt;br /&gt;
showInputField() is a sort of converter customfieldstructure -&amp;gt; HTML input field.&lt;br /&gt;
&lt;br /&gt;
Use the &#039;&#039;&#039;showInputField()&#039;&#039;&#039; method to print the right input form for a custom field.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;showInputField($field, $currentvalue=null, $moreparam=&#039;&#039;, $ajax_php_callback=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
showInputField() takes 4 parameters:&lt;br /&gt;
&lt;br /&gt;
* $field is a CustomFields structure object (eg: returned by fetchFieldStruct()) and is necessary to detect the right datatype and the possible values.&lt;br /&gt;
* $currentvalue is the current value if you want to preselect a value (for example if the user already defined a value, you can reload it here). This is optional, in case it is not defined, either a null/empty value will be chosen, or either the default value defined when creating the custom field.&lt;br /&gt;
* $moreparam is used to add more HTML attributes, if you want for example to add some css styling or whatever you want.&lt;br /&gt;
* $ajax_php_callback is optional and allows to specify the relative url to the php callback script. If specified, an AJAX script will be automatically attached to this field (see AJAX-PHP callback below).&lt;br /&gt;
&lt;br /&gt;
showInputField() only prints ONE HTML input field, thus you have to enclose it/them by your own HTML form.&lt;br /&gt;
&lt;br /&gt;
eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print &#039;&amp;lt;form method=&amp;quot;get&amp;quot; action=&amp;quot;?&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
print &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;customtoken&amp;quot; value=&amp;quot;ABCDEFGH&amp;quot; /&amp;gt;&#039;; // example of a custom hidden HTML input field, that can be used to pass a token for example&lt;br /&gt;
// Loop over each custom fields (you have to load them before with $customfields-&amp;gt;fetchAllFieldsStruct()&lt;br /&gt;
foreach ($customfields-&amp;gt;fields as $field) {&lt;br /&gt;
    print $customfields-&amp;gt;showInputField($field, $currentvalue=null, $moreparam=&#039;&#039;);&lt;br /&gt;
}&lt;br /&gt;
print &#039;&amp;lt;/form&amp;gt;&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can also store the resulting HTML input field and process it by a regexp for example:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$myhtmlfield = $customfields-&amp;gt;showInputField($field, $currentvalue=null, $moreparam=&#039;&#039;);&lt;br /&gt;
preg_replace($myhtmlfield, ..., ...);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: for constrained fields, if the name follows the convention of &#039;&#039;&#039;Smart Value Substitution&#039;&#039;&#039; (ie: remote columns names are included in the custom field&#039;s name), showInputField() will &#039;&#039;&#039;automatically fetch the remote values, format them, and present them&#039;&#039;&#039; in a HTML select.&lt;br /&gt;
&lt;br /&gt;
=== Printing an input form for a custom field ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;showInputForm()&#039;&#039;&#039; is a function used to print a single (and only one!) custom field with an adapted HTML input field, but enclosed by a HTML form. This is used for example when you want to allow to edit a single custom field but not the others.&lt;br /&gt;
&lt;br /&gt;
showInputForm() is in fact a wrapper for showInputField(), thus it will call showInputField() and simply enclose it by a form.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;showInputForm($id, $field, $currentvalue=null, $idvar=&#039;id&#039;, $page=null, $moreparam=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== AJAX-PHP callback ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;showInputFieldAjax()&#039;&#039;&#039; will output an AJAX script (using jQuery) to send and receive data to and from a specified php script. The AJAX script will automatically manage HTML form input&#039;s updating as long as the received data follows some convention.&lt;br /&gt;
&lt;br /&gt;
An AJAX script can be attached to any field (including non-CustomFields) by using the method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;showInputFieldAjax($id, $php_callback_url, $on_func=&amp;quot;change&amp;quot;, $request_type=&amp;quot;post&amp;quot;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
* $id is the HTML id of the field you want to attach the AJAX to.&lt;br /&gt;
* $phpcallback the relative path from Dolibarr&#039;s htdocs root folder to the php script that will receive and send back data.&lt;br /&gt;
* $on_func is the Javascript or jQuery event that should trigger the AJAX script (by default on change).&lt;br /&gt;
* $request_type is the HTML query type that will be used to send the data to the PHP callback (either &amp;quot;get&amp;quot; or &amp;quot;post&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
The AJAX script will automatically manage sending/receiving of data:&lt;br /&gt;
&lt;br /&gt;
* automatically send all form&#039;s inputs values, as well as the current calling field&#039;s name and value. The sending happens on change by default, but another event can be set using the $on_event argument. Data will be sent using standard GET or POST.&lt;br /&gt;
* Upon reception of data from the php script, the AJAX script will automatically parse the data and update the HTML fields.&lt;br /&gt;
&lt;br /&gt;
The PHP callback script must send back to AJAX the data in JSON encoded format, and with with a specific format:&lt;br /&gt;
&lt;br /&gt;
* the data must be an associative array, where each entry&#039;s key is the name (not ID!) of an HTML field to update.&lt;br /&gt;
* each entry&#039;s value is an associative array, where each sub-entry&#039;s key is the action to do. Available actions: &#039;options&#039;, &#039;value&#039;, &#039;html&#039;, &#039;alert&#039;.&lt;br /&gt;
* each sub-entry&#039;s value is the value for this action and HTML field.&lt;br /&gt;
&lt;br /&gt;
Thus, this approach allows to generically support a wide range of updating actions, managed automatically.&lt;br /&gt;
&lt;br /&gt;
You, the implementer, have just to edit the PHP callback script and return an associative array defining what action you want for each field you want to update. This is exactly what has been implemented in the Custom AJAX Functions library, which simplifies even more your job by managing automatically the step described here (generating AJAX and formatting data), so that you can focus on &#039;&#039;&#039;making the data&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Printing custom fields in documents ==&lt;br /&gt;
&lt;br /&gt;
The API can also automatically manage the printing and formatting of custom fields in your ODT, PDF and HTML documents.&lt;br /&gt;
&lt;br /&gt;
=== Printing in HTML ===&lt;br /&gt;
&lt;br /&gt;
You can either just print the value as-is, or pass them through the printField() or simpleprintField() function to format them (very useful for constrained fields) - see below.&lt;br /&gt;
&lt;br /&gt;
=== Printing in documents ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;printField()&#039;&#039;&#039; is used to properly print and translate a custom field&#039;s record&#039;s value. Generally, you can just print the custom field&#039;s value as-is, but in a few cases (eg for boolean types, or constrained fields), using the printField() method will more properly print the value.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;printField($field, $value, $outputlangs=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
* $field is the custom field&#039;s SQL structure as an object (eg: returned by fetchFieldStruct()).&lt;br /&gt;
* $value is the custom field&#039;s value for the current record (eg: $customfields-&amp;gt;fields-&amp;gt;myfield).&lt;br /&gt;
* $outputlangs is the Translate object for the target language.&lt;br /&gt;
&lt;br /&gt;
For example, with a constrained field with Smart Value Substitution, instead of printing the id like 1, it will print the full name of user like FirstName Name.&lt;br /&gt;
&lt;br /&gt;
This method will also translate a value if a translation is found in the language files.&lt;br /&gt;
&lt;br /&gt;
Another method &#039;&#039;&#039;printFieldPDF()&#039;&#039;&#039; can be used to properly print a value for a PDF (properly encoding the characters).&lt;br /&gt;
&lt;br /&gt;
=== Simple printing in documents ===&lt;br /&gt;
&lt;br /&gt;
If you don&#039;t want to have to manually fetch the structure of every custom field whose you want to print the value, you can then use a simplifier method called &#039;&#039;&#039;simpleprintField()&#039;&#039;&#039; which will take as input a custom field name instead of a custom field SQL structure object.&lt;br /&gt;
&lt;br /&gt;
This method is just a simplifier (Facade) wrapper for the printField() method, and thus will have the same advantages (fetch constrained fields, values translation, etc.).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;simpleprintField($fieldname, $value, $outputlangs=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
* $fieldname is the custom field&#039;s name (eg: &amp;quot;myfield&amp;quot;).&lt;br /&gt;
* $value is the custom field&#039;s value for the current record (eg: $customfields-&amp;gt;fields-&amp;gt;myfield).&lt;br /&gt;
* $outputlangs is the Translate object for the target language.&lt;br /&gt;
&lt;br /&gt;
Another method &#039;&#039;&#039;simpleprintFieldPDF()&#039;&#039;&#039; can be used to properly print a value for a PDF (properly encoding the characters).&lt;br /&gt;
&lt;br /&gt;
=== Find the label ===&lt;br /&gt;
&lt;br /&gt;
Finding the label of a custom field might not be as simple as it seems. First, values of properties objects may be easily used, but their name (key) is a worst job. Secondly, custom fields&#039; labels can be translated, and the following functions are expressly made for that purpose. Thirdly, for PDF and some other documents you have to encode the characters if you want them to properly be drawn.&lt;br /&gt;
&lt;br /&gt;
So if you want to print a custom field&#039;s label with translation (multilingual) support, just use the &#039;&#039;&#039;findLabel()&#039;&#039;&#039; method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$translatedfieldname = $customfields-&amp;gt;findLabel($fieldname, $outputlangs = &#039;&#039;); // where $fieldname is just a custom field label (eg: cf_mycustomfield or mycustomfield, both will work) and $outputlangs is the target language object (see the Translate class native of Dolibarr)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As for the other printing methods, you also have the &#039;&#039;&#039;findLabelPDF()&#039;&#039;&#039; wrapper method to easily print labels that with characters encoded for PDF.&lt;br /&gt;
&lt;br /&gt;
= Facade API =&lt;br /&gt;
&lt;br /&gt;
The Facade API was made to ease the usage of the CustomFields class with the Dolibarr system. It provides a neat way to do many operations with single commands, and will automatically manage the loading of the CustomFields class, and its usage with many optimizations.&lt;br /&gt;
&lt;br /&gt;
== customfields_fill_object() ==&lt;br /&gt;
&lt;br /&gt;
You can load the Facade API and then use the &#039;&#039;&#039;customfields_fill_object()&#039;&#039;&#039; to automatically fetch the fields structures and the records, and also automatically link to remote fields when a constrained field is found. This is an all-in-one solution to easily fetch CustomFields datas, and is also optimized to fully use caching and other special properties of the CustomFields class.&lt;br /&gt;
&lt;br /&gt;
Concretely, you just have to include the Facade API, then pass the customfields_fill_object() with a Dolibarr $object, and the Facade API will return a CustomFields object with appended the custom fields records and also the custom fields&#039; SQL structures.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$customfields = customfields_fill_object($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* $object will then include the custom fields records associated with this object (eg: $object-&amp;gt;customfields-&amp;gt;cf_yourfield).&lt;br /&gt;
* $customfields will be a CustomFields object containing the fields structures (eg: $customfields-&amp;gt;fields-&amp;gt;yourfield).&lt;br /&gt;
&lt;br /&gt;
You can also call any CustomFields function if you need to using the $customfields object, eg: $customfields-&amp;gt;simpleprintField($object-&amp;gt;customfields-&amp;gt;cf_yourfield);&lt;br /&gt;
&lt;br /&gt;
Note: $object can also be a dummy object containing only two properties that are required: $object-&amp;gt;id and $object-&amp;gt;table_element.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will now describe the full specification of this function:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$customfields = customfields_fill_object(&amp;amp;$object,$fromobject = null, $outputlangs = null, $prefix = null,$pdfformat = false);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* $object is the object where the customfields will be saved&lt;br /&gt;
* $fromobject is the object from where the customfields will be fetched (this can be a different module than $object, or can be null to be the same as $object)&lt;br /&gt;
* $outputlangs is the target language for translation of fields value (if empty, the default current global translation object will be used)&lt;br /&gt;
* $prefix allows you to save the custom fields in a sub-object. By default, when $prefix=null, custom fields are saved in the path $object-&amp;gt;customfields, but when you specify a prefix, custom fields will be saved inside $object-&amp;gt;customfields-&amp;gt;$prefix. This may be useful if you want to store multiple object&#039;s custom fields or records in the same $object.&lt;br /&gt;
* $pdfformat defines if you want a special formatting for the values: if null, there&#039;s no formatting, if false, there is a simple formatting (eg: translation of fields&#039; values), if true then the formatting will be adapted for PDF templating.&lt;br /&gt;
&lt;br /&gt;
For more informations on pratical usages of this function, please [[Module_CustomFields#Implementing_in_php_code_.28dolibarr_core_modules_or_your_own_module.29|read the user documentation on how to implement CustomFields in php code]] or [[Module_CustomFields_Cases#Linking_Dolibarr_objects_from_two_different_modules|read an example case of linking two objects of different modules together]].&lt;br /&gt;
&lt;br /&gt;
== customfields_fill_object_lines() ==&lt;br /&gt;
&lt;br /&gt;
Similar to customfields_fill_object() for normal custom fields, there is also a &#039;&#039;&#039;customfields_fill_object_lines()&#039;&#039;&#039; to fetch custom fields associated with lines of products/services. This function is also optimized and it is strongly advised to use it and to not try to fetch manually this kind of custom fields (ie: associated with lines of products/services rather than objects), because there are a lot of optimizations going on behind the scene and that are very hard to do manually.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields for lines of products/services (you can then access them by doing $object-&amp;gt;customfields-&amp;gt;lines-&amp;gt;$lineid-&amp;gt;cf_yourfield)&lt;br /&gt;
$customfields = customfields_fill_object_lines($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Requirements: $object can be a dummy object but must contain at least 2 properties: $object-&amp;gt;id and $object-&amp;gt;table_element_line.&lt;br /&gt;
&lt;br /&gt;
For more informations on pratical usages of this function, please [[Module_CustomFields#Implementing_in_php_code_.28dolibarr_core_modules_or_your_own_module.29|read the user documentation on how to implement CustomFields in php code]].&lt;br /&gt;
&lt;br /&gt;
= Architecture =&lt;br /&gt;
Here is a full list of the CustomFields packaged files with a short description (for a more in-depth view just crawl the source files, they are full of comments):&lt;br /&gt;
&lt;br /&gt;
== Core files ==&lt;br /&gt;
files that are necessary for the CustomFields to work, they contains the core functions&lt;br /&gt;
&lt;br /&gt;
* /customfields/admin/customfields.php --- Administrator&#039;s configuration panel : this is where you create and manage the custom fields definitions&lt;br /&gt;
* /customfields/class/actions_customfields.class.php --- Hooks class : used to hook into Dolibarr core modules without altering any core file (can be used to hook into your own modules too). Also used to preprocess the parameters before calling the printforms library.&lt;br /&gt;
* /customfields/class/customfields.class.php --- Core class : every database action is made here in this class. You can find some printing functions because they are very generic.&lt;br /&gt;
* /customfields/conf/conf_customfields.lib.php --- Configuration file : contains the main configurable variables to adapt CustomFields to your needs or to expand its support to other modules and more native sql types.&lt;br /&gt;
* /customfields/conf/conf_customfields_func.lib.php --- Auxiliary functions library to read and manage config variables&lt;br /&gt;
* /customfields/core/modules/modCustomFields.class.php --- Dolibarr&#039;s module definition file : this is a core file necessary for Dolibarr to recognize the module and to declare the hooks to Dolibarr (but it does not store anything else than meta-informations).&lt;br /&gt;
* /customfields/core/substitutions/functions_customfields.lib.php --- CustomFields substitution class for ODT generation : necessary to support customfields tags in ODT files&lt;br /&gt;
* /customfields/core/triggers/interface_50_modCustomFields_SaveFields.class.php --- Core triggers file : this is where the actions on records are managed. This is an interface between other modules and CustomFields management. You should not modify this file but you should add your triggers in $triggersarray inside conf_customfields.lib.php, but if you really have a special need and know what you do, you can edit this file to directly add your own triggers. Also, if CustomFields are shown in the forms but cannot be modified nor saved, then probably this trigger file is not detected by Dolibarr (maybe you can try to lower the number 50 to raise the priority?).&lt;br /&gt;
* /customfields/fields/customfields_fields_extend.lib.php.example --- Overloading functions library with users functions: this is where implementers can use their own php code to manage the custom fields they want (one function per action and per field).&lt;br /&gt;
* /customfields/fields/customfields_fields_ajax_custom.lib.php.example --- Custom AJAX functions library with users functions: this is where implementers can use their own php code to manage the custom fields cascading and AJAX calls, similarly to the Overloading functions library (one function per action and per field).&lt;br /&gt;
* /customfields/langs/code_CODE/customfields.lang --- Core language file : this is where you can translate the admin config panel (data types names, labels, descriptions, etc.)&lt;br /&gt;
* /customfields/langs/code_CODE/customfields-user.lang --- User defined language file : this is where you can store the labels and values of your custom fields (see the related chapter)&lt;br /&gt;
* /customfields/lib/customfields_aux.lib.php --- Simplifier library to easily use CustomFields class to populate an $object with custom fields datas (Facade design pattern). This is what you should use for most of your needs.&lt;br /&gt;
* /customfields/lib/customfields_printforms.lib.php --- Core printing library for records : contains only printing functions, there&#039;s not really any core functions inside but it allows to manage the printing of the custom fields records and their editing.&lt;br /&gt;
* /customfields/lib/customfields_ajax_wrapper.lib.php --- AJAX wrapper library to automatically manage cascading at objects creation. This isn&#039;t really necessary for CustomFields to function, this is more an addition to allow dynamical AJAX updating, but CustomFields core functionalities can work without this library.&lt;br /&gt;
* /customfields/sql/*.sql --- Unused (the tables are created directly via a function in the customfields.class.php) - but the sql files are still valid, they were used as squeletton in the first designs.&lt;br /&gt;
&lt;br /&gt;
== PDFTest module ==&lt;br /&gt;
optional module to append a page listing all custom fields values on newly generated PDF documents. To be used to test if custom fields are working correctly before making one&#039;s own template.&lt;br /&gt;
&lt;br /&gt;
* /customfieldspdftest/class/actions_customfieldspdftest.class.php --- Main functions library and hooks class, to hook onto pdf generation. Contains the PDFTest functions.&lt;br /&gt;
* /customfieldspdftest/core/modules/modCustomFieldsPDFTest.class.php --- Module descriptor for Dolibarr&lt;br /&gt;
&lt;br /&gt;
== Runtime outline ==&lt;br /&gt;
&lt;br /&gt;
Here is an outline of how CustomFields works: what functions will be called and why:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At admin page:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* No external function call (apart for some icons or needed admin functions like for tabs), everything that is done are sql queries via CustomFields class (to fetch and modify the sql columns in the customfields tables).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At printing:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Module&#039;s hook formObjectOptions calls Dolibarr&#039;s hookmanager&lt;br /&gt;
* Hookmanager calls CustomFields /class/actions_customfields.class.php, function formObjectOptions() of CustomFields&lt;br /&gt;
* Preprocess a few parameters (mainly the $action) and calls customfields_print_forms()&lt;br /&gt;
* customfields_print_forms() preprocess all the hook&#039;s parameters and assimilate them with the CustomFields&#039;s configuration parameters (there&#039;s no connection to the database at this stage, only parameters preprocessing), then calls /lib/customfields_printforms.lib.php, functions customfields_print_creation_form() or customfields_print_datasheet_form&lt;br /&gt;
* both customfields_print_creation_form() lookup the database using CustomFields class and prints empty custom fields; customfields_print_datasheet_form() lookup the database using CustomFields class, fetch the values of the custom fields and prints them;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At edition:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Same steps as &#039;&#039;&#039;At printing&#039;&#039;&#039;&lt;br /&gt;
* then customfields_print_datasheet_form() process the custom field being edited differently than the others: if editing, it prints the html input field (to enter a new value), or if saving it just saves the data into the database by calling the CUSTOMFIELDS_MODIFY trigger and then continue to print the other fields normally.&lt;br /&gt;
* CUSTOMFIELDS_MODIFY trigger calls $customfields-&amp;gt;update($newrecord);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At creation (saving):&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Module&#039;s creation trigger MYMODULE_CREATE calls Dolibarr&#039;s run_trigger()&lt;br /&gt;
* Dolibarr run_trigger() calls CustomFields trigger file /core/trigger/interface_50_modCustomFields_SaveFields.class.php, function run_trigger()&lt;br /&gt;
* CustomFields&#039;s run_trigger() assimilate the given parameters with the CustomFields&#039;s config parameters, and if the trigger is found in $triggersarray, then the appropriate action is taken (by default: _CREATE or another standard action if recognized), if not in $triggersarray, the function will still try to automatically detect the appropriate action by detecting MYMODULE_ALISTOFACTIONS with a regular expression.&lt;br /&gt;
* The appropriate action is then done (creation in this case) by calling $customfields-&amp;gt;create($record).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At edition or creation of product lines:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Similar to &#039;&#039;&#039;At edition&#039;&#039;&#039; but with hooks: formCreateProductOptions and formEditProductOptions.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At ODT document generation and substitution:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Module&#039;s ODT generator calls for Dolibarr&#039;s function complete_substitution_array(), which then calls for CustomFields substitution file /core/substitution/functions_customfields.lib.php, function customfields_completesubstitutionarray()&lt;br /&gt;
* CustomFields&#039;s function customfields_completesubstitutionarray() calls the simplifier library customfields_aux.lib.php, function customfields_fill_object() and make the substitution inside the $substitutionarray.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At ODT document generation and substitution for product lines:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Same as the previous point but this function is called for every product lines, and thus the data is cached. Also the simplifier function is here called customfields_fill_object_lines(), and the CustomFields&#039;s substitution function called customfields_completesubstitutionarray_lines().&lt;br /&gt;
&lt;br /&gt;
= How to add the support of a new module =&lt;br /&gt;
&lt;br /&gt;
There are two cases: either the module already implements hooks, and you just have to configure CustomFields to support it, or either the module does not implements hooks nor triggers.&lt;br /&gt;
&lt;br /&gt;
This chapter will guide you through the steps to make any module supported by CustomFields (be it a Dolibarr core module or your own module).&lt;br /&gt;
&lt;br /&gt;
== Preliminary test ==&lt;br /&gt;
&lt;br /&gt;
This preliminary test will allow you to know at a glance how much work needs to be done in order to make your module supported by CustomFields, and it will also give you all the config parameters if they are available.&lt;br /&gt;
&lt;br /&gt;
Important note: this test will only give data about hooks, which is the main part to make CustomFields work for your module. But for triggers, there is no way to test, so you will have to check the triggers manually (either by testing if the custom fields are saved at creation page, or by looking at the source code of the objects classes).&lt;br /&gt;
&lt;br /&gt;
Edit the php script of the module (check the URL when you access the module&#039;s datasheet or creation page - attention both are two different pages, so you have to do this twice), then put this at the very &#039;&#039;&#039;end&#039;&#039;&#039; of the php script:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// CustomFields debuglines to show config parameters for current module&lt;br /&gt;
print(&amp;quot;&amp;lt;pre&amp;gt;&amp;quot;); // html nice output&lt;br /&gt;
print(&amp;quot;table_element: {$object-&amp;gt;table_element} - table_element_line: {$object-&amp;gt;table_element_line} - contexts: &amp;quot;);&lt;br /&gt;
print_r($hookmanager-&amp;gt;contextarray);&lt;br /&gt;
print(&amp;quot; - conf hooks_modules: &amp;quot;);&lt;br /&gt;
print_r($conf-&amp;gt;hooks_modules);&lt;br /&gt;
print(&amp;quot; - hooks attached: &amp;quot;);&lt;br /&gt;
print_r($hookmanager-&amp;gt;hooks);&lt;br /&gt;
print(&amp;quot; - user rights: &amp;quot;);&lt;br /&gt;
print_r($user-&amp;gt;rights);&lt;br /&gt;
print(&amp;quot;&amp;lt;/pre&amp;gt;&amp;quot;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now reload the page, and scroll at the very bottom. You should see some informations about the module.&lt;br /&gt;
&lt;br /&gt;
Depending on the data available, you will know what you have to do:&lt;br /&gt;
&lt;br /&gt;
* If all the datas is available, then it&#039;s ok, jump to [[Module_CustomFields_Dev#Configuring_CustomFields_to_support_the_module]] and copy these datas to the parameters of conf_customfields.lib.php&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;table_element&#039;&#039;&#039;: $object is not defined, try to find how is called the main object variable and replace $object by this variable in the debug code given above (eg: in Agenda, the object variable is called $agenda and $agendacomm).&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;table_element_line&#039;&#039;&#039;: either there&#039;s no products lines for this module, either the main object variable is not called $object (see the previous point). One last case is that the object is not standard and does not store table_element_line (the name of the table that stores its products lines), in which case you have to manually add it in the class (eg: $this-&amp;gt;table_element_line = &#039;something&#039;).&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;contexts&#039;&#039;&#039;: the hookmanager is not defined, and probably there&#039;s no hook at all in this module, so you&#039;ll have to implement them by yourself. Jump to [[Module_CustomFields_Dev#Hooks]].&lt;br /&gt;
&lt;br /&gt;
* If there is a list of &#039;&#039;&#039;contexts&#039;&#039;&#039; but the module cannot be found in the list: the hookmanager is defined but the module has not defined a context, you still have to implement the hooks. Jump to [[Module_CustomFields_Dev#Hooks]].&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;conf hooks_modules&#039;&#039;&#039;: this variable is not necessary, empty or not it doesn&#039;t matter. But if you see customfields and/or customfieldspdftest inside the list, it is a confirmation that CustomFields indeed is hooking the module correctly.&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;hooks attached&#039;&#039;&#039;, or CustomFields cannot be found in the list: CustomFields has not been properly configured to hook to this module, or you forgot to disable then re enable CustomFields in order to update the CustomFields&#039;s hooks. Jump to [[Module_CustomFields_Dev#Configuring_CustomFields_to_support_the_module]].&lt;br /&gt;
&lt;br /&gt;
* If there is no &#039;&#039;&#039;$user-&amp;gt;rights&#039;&#039;&#039;: there is either no standard security implemented in this module, or either the variable is called with another name. If there is no security, you don&#039;t need to configure it in CustomFields, but if there is a security, you need to find how the variable is called (like $object can get another name sometimes).&lt;br /&gt;
&lt;br /&gt;
* If there is &#039;&#039;&#039;$user-&amp;gt;rights&#039;&#039;&#039; but the module can&#039;t be found in the list: either the name for the rights is different (this often happens, the rights array is not yet standardized), or either the rights are hardcoded into the module&#039;s code (rights should be announced to the $user-&amp;gt;rights array, but sometimes it happens that the dev forgot to do so, and thus the right is entirely hardcoded and you can&#039;t find it in the dynamic rights array). In any of these two cases, you will have to manually read the module&#039;s code to find the right. Also, maybe there is no security implemented, but since $user-&amp;gt;rights is declared this is less probable.&lt;br /&gt;
&lt;br /&gt;
Here is an example of output on a correctly hooked module (invoice):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
table_element: facture - table_element_line: facturedet - contexts: Array&lt;br /&gt;
(&lt;br /&gt;
    [0] =&amp;gt; searchform&lt;br /&gt;
    [1] =&amp;gt; leftblock&lt;br /&gt;
    [2] =&amp;gt; toprightmenu&lt;br /&gt;
    [3] =&amp;gt; invoicecard&lt;br /&gt;
)&lt;br /&gt;
 - conf hooks_modules: Array&lt;br /&gt;
(&lt;br /&gt;
    [customfields] =&amp;gt; invoicecard:propalcard:productcard:ordercard:thirdpartycard:contactcard:ordersuppliercard:invoicesuppliercard:membercard:actioncard:projectcard:projecttaskcard:contractcard:interventioncard:doncard:tripsandexpensescard:taxvatcard:expeditioncard:invoicecard:propalcard:ordercard:ordersuppliercard:invoicesuppliercard&lt;br /&gt;
    [customfieldspdftest] =&amp;gt; Array&lt;br /&gt;
        (&lt;br /&gt;
            [0] =&amp;gt; pdfgeneration&lt;br /&gt;
            [1] =&amp;gt; ordersuppliercard&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
)&lt;br /&gt;
 - hooks attached: Array&lt;br /&gt;
(&lt;br /&gt;
    [invoicecard] =&amp;gt; Array&lt;br /&gt;
        (&lt;br /&gt;
            [customfields] =&amp;gt; ActionsCustomFields Object&lt;br /&gt;
                (&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
)&lt;br /&gt;
 - user rights: stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [user] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [user] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [lire] =&amp;gt; 1&lt;br /&gt;
                    [creer] =&amp;gt; 1&lt;br /&gt;
                    [password] =&amp;gt; 1&lt;br /&gt;
                    [supprimer] =&amp;gt; 1&lt;br /&gt;
                    [export] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [self] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [creer] =&amp;gt; 1&lt;br /&gt;
                    [password] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [user_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [readperms] =&amp;gt; 1&lt;br /&gt;
                    [write] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [self_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [readperms] =&amp;gt; 1&lt;br /&gt;
                    [writeperms] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [group_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [read] =&amp;gt; 1&lt;br /&gt;
                    [readperms] =&amp;gt; 1&lt;br /&gt;
                    [write] =&amp;gt; 1&lt;br /&gt;
                    [delete] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    [facture] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [lire] =&amp;gt; 1&lt;br /&gt;
            [creer] =&amp;gt; 1&lt;br /&gt;
            [invoice_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [unvalidate] =&amp;gt; 1&lt;br /&gt;
                    [send] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [valider] =&amp;gt; 1&lt;br /&gt;
            [paiement] =&amp;gt; 1&lt;br /&gt;
            [supprimer] =&amp;gt; 1&lt;br /&gt;
            [facture] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [export] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    [propale] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [lire] =&amp;gt; 1&lt;br /&gt;
            [creer] =&amp;gt; 1&lt;br /&gt;
            [valider] =&amp;gt; 1&lt;br /&gt;
            [propal_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [send] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [cloturer] =&amp;gt; 1&lt;br /&gt;
            [supprimer] =&amp;gt; 1&lt;br /&gt;
            [export] =&amp;gt; 1&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    [produit] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [lire] =&amp;gt; 1&lt;br /&gt;
            [creer] =&amp;gt; 1&lt;br /&gt;
            [supprimer] =&amp;gt; 1&lt;br /&gt;
            [export] =&amp;gt; 1&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    etc... a lot of other rights...&lt;br /&gt;
&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When you are finished with implementing the support of CustomFields in the module, you should delete these debug lines.&lt;br /&gt;
&lt;br /&gt;
== Implementing the support of CustomFields in the module ==&lt;br /&gt;
&lt;br /&gt;
If there is a module you want to implement the support of CustomFields, but does not yet has the required components, here is a guide on how to implement these components.&lt;br /&gt;
&lt;br /&gt;
=== Hooks ===&lt;br /&gt;
&lt;br /&gt;
Implementing hooks will allow custom fields to appear on your module&#039;s page. They will also be editable and they will save the data. For creation, you need to use a trigger (see the subchapter below).&lt;br /&gt;
&lt;br /&gt;
First you should be familiar with the way that hooks are implemented in modules. If you don&#039;t, please first [[Hooks_system#Implementation|read the hooks documentation]].&lt;br /&gt;
&lt;br /&gt;
An example case of implementation of hooks into a Dolibarr core file can be found in the [[Module_CustomFields_Cases|CustomFields Example Cases wiki page]].&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Datasheet hook&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
To implement CustomFields in your module, you must initialize the hook manager, then use the following hook context:&lt;br /&gt;
* formObjectOptions&lt;br /&gt;
&lt;br /&gt;
This hook must be placed everywhere in your code where you want custom fields to appear (eg: on the creation page and datasheet view page).&lt;br /&gt;
&lt;br /&gt;
Note: Generally, you will have to place the hook just before the end of a table tag (&#039;&#039;&#039;&amp;lt;/table&amp;gt;&#039;&#039;&#039;), so if you don&#039;t know where to place it (eg: if you&#039;re implementing CustomFields on a Dolibarr core module), just search for this tag in the page shown in the url (eg: /htdocs/commande/fiche.php).&lt;br /&gt;
&lt;br /&gt;
Eg: First load the hookmanager at the beginning of your php script:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Initialize technical object to manage hooks of thirdparties. Note that conf-&amp;gt;hooks_modules contains array array&lt;br /&gt;
include_once(DOL_DOCUMENT_ROOT.&#039;/core/class/hookmanager.class.php&#039;);&lt;br /&gt;
$hookmanager=new HookManager($db);&lt;br /&gt;
$hookmanager-&amp;gt;initHooks(array(&#039;yourmodule&#039;));&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then place the hook as many times as required:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// ... a lot of php code ...&lt;br /&gt;
&lt;br /&gt;
print &#039;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&#039;; // end of the last row of a table&lt;br /&gt;
&lt;br /&gt;
// Other attributes&lt;br /&gt;
$parameters=array(&#039;colspan&#039; =&amp;gt; &#039; colspan=&amp;quot;3&amp;quot;&#039;); // specifying the colspan is optional but will help CustomFields know how to correctly print the table&lt;br /&gt;
$reshook=$hookmanager-&amp;gt;executeHooks(&#039;formObjectOptions&#039;,$parameters,$object,$action);    // Note that $action and $object may have been modified by hook&lt;br /&gt;
&lt;br /&gt;
print &#039;&amp;lt;/table&amp;gt;&#039;; // end of the table, this is what you&#039;re looking for, place the hook just above&lt;br /&gt;
&lt;br /&gt;
// ... a lot of php code ... redo the same if you find another &amp;lt;/table&amp;gt; and there&#039;s a page where there&#039;s no custom fields but you want to place custom fields on there&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note2: If after implementing the hook you receive an error or custom fields don&#039;t appear, you may have to change $object to another variable, depending on what is called the main object (eg: in Agenda it&#039;s called $action and $actioncomm).&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Lines hooks&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
For product lines, you should anyway use the product lines template files: htdocs/core/tpl/freeproductline_edit.tpl.php&lt;br /&gt;
&lt;br /&gt;
If you don&#039;t, you can still manually implement the required hooks, what is required is that you supply $line (which is the current line object being processed) in the $parameters array:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$parameters=array(&#039;line&#039;=&amp;gt;$line,&#039;fk_parent_line&#039;=&amp;gt;$line-&amp;gt;fk_parent_line);&lt;br /&gt;
&lt;br /&gt;
echo $hookmanager-&amp;gt;executeHooks(&#039;formEditProductOptions&#039;,$parameters,$this,$action);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hooks names:&lt;br /&gt;
* formEditProductOptions&lt;br /&gt;
* formCreateProductOptions ($line is not required here, because we create a new product line! you can leave $parameters=array(); empty).&lt;br /&gt;
Note: there&#039;s no hook for viewing, because showing the customfields will clutter the visual field, but if you really want you can also do it by adding a formViewProductOptions hook.&lt;br /&gt;
&lt;br /&gt;
=== Triggers ===&lt;br /&gt;
&lt;br /&gt;
First you need to know how to implement triggers in Dolibarr. For more informations about how to implement triggers in your module, please read the [[Triggers#Manage_and_create_a_new_trigger.27s_action|triggers wiki page]].&lt;br /&gt;
&lt;br /&gt;
An example case of implementation of hooks into a Dolibarr core file can be found in the [[Module_CustomFields_Cases|CustomFields Example Cases wiki page]].&lt;br /&gt;
&lt;br /&gt;
Implementing triggers on your module will allow CustomFields to save the fields at creation (datasheet edition is already managed by hooks).&lt;br /&gt;
&lt;br /&gt;
You need to implement 3 triggers:&lt;br /&gt;
- on creation of object - eg: YOURMODULE_CREATE&lt;br /&gt;
- on creation of lines - eg: YOURMODULELINE_INSERT (optional, only if your module has product lines)&lt;br /&gt;
- on update of lines - eg: YOURMODULELINE_UPDATE (optional, only if your module has product lines)&lt;br /&gt;
&lt;br /&gt;
Note: you need to supply the sql id of the newly created object before calling the trigger.&lt;br /&gt;
&lt;br /&gt;
Eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$this-&amp;gt;id = $this-&amp;gt;db-&amp;gt;last_insert_id(MAIN_DB_PREFIX.&amp;quot;yourmodule_table_element&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
// Appel des triggers&lt;br /&gt;
include_once(DOL_DOCUMENT_ROOT . &amp;quot;/core/class/interfaces.class.php&amp;quot;);&lt;br /&gt;
$interface=new Interfaces($this-&amp;gt;db);&lt;br /&gt;
$result=$interface-&amp;gt;run_triggers(&#039;YOURMODULE_CREATE&#039;,$this,$user,$langs,$conf);&lt;br /&gt;
if ($result &amp;lt; 0) {&lt;br /&gt;
    $error++; $this-&amp;gt;errors=$interface-&amp;gt;errors;&lt;br /&gt;
}&lt;br /&gt;
// Fin appel triggers&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note2: if it&#039;s a Dolibarr core module, you can easily find the right place to put the trigger by searching for &#039;create sql&#039; in the class subfolder (eg: URL is /htdocs/commande/fiche.php, the class is in /htdocs/commande/class/commande.class.php).&lt;br /&gt;
&lt;br /&gt;
Note3: CustomFields also supports a few other triggers like _CLONE, _UPDATE, _MODIFY, but they are not required and may not work depending on how your module works to manage these actions.&lt;br /&gt;
&lt;br /&gt;
== Configuring CustomFields to support the module ==&lt;br /&gt;
&lt;br /&gt;
=== Preliminary work ===&lt;br /&gt;
&lt;br /&gt;
You need to get the parameters of this module, and you will then put them in the CustomFields configuration file (conf_customfields.lib.php).&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Getting the parameters automatically&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
You can use the [[Module_CustomFields_Dev#Preliminary_test|preliminary test]] to easily get all those parameters.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Getting the parameters manually&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
To configure CustomFields to support a module, you need at least 5 parameters:&lt;br /&gt;
* the module&#039;s table_name (name of the sql table where the module stores its datas)&lt;br /&gt;
* the module&#039;s context (name given by the module to the hookmanager)&lt;br /&gt;
* the module&#039;s idvar (name of the variable that holds the sql row id of the record)&lt;br /&gt;
* the module&#039;s trigger action(s) (triggers on creation and update, see the related chapter above)&lt;br /&gt;
* the module&#039;s rights (for security purposes)&lt;br /&gt;
&lt;br /&gt;
To get these parameters manually, you can take a look inside the code of the module&#039;s files you want to implement:&lt;br /&gt;
* module&#039;s table name: either find it by yourself in the database (looks like llx_themodulename) or by printing it in actions_customfields.class.php by adding &#039;&#039;&#039;print_r($parameters)&#039;&#039;&#039; and search for $parameters-&amp;gt;table_element&lt;br /&gt;
* module&#039;s context: search for &amp;quot;callHooks(&amp;quot; without the quotes or take a look at the wiki: http://wiki.dolibarr.org/index.php/Hooks_system&lt;br /&gt;
or you can get it by printing it in the /htdocs/customfields/class/actions_customfields.class.php by adding print_r($parameters) and search for $parameters-&amp;gt;context&lt;br /&gt;
* module&#039;s trigger actions: search for &amp;quot;run_triggers(&amp;quot; or take a look at the wiki: http://wiki.dolibarr.org/index.php/Triggers#List_of_known_triggers_actions&lt;br /&gt;
* module&#039;s idvar: look at the url when you access one of the module&#039;s record, and you should see something like fiche.php?xxxid=12 - you need the xxxid (eg: socid, facid, etc.).&lt;br /&gt;
* module&#039;s security: in the page shown in URL when accessing the module&#039;s datasheet, edit this file and add &#039;&#039;&#039;print($user-&amp;gt;rights)&#039;&#039;&#039;, then in the list try to find the correct right (generally something like $user-&amp;gt;modulename-&amp;gt;creer). You can also try to search for $user-&amp;gt;rights in the sourcecode of this module.&lt;br /&gt;
&lt;br /&gt;
With these values, edit the config file (/htdocs/customfields/conf/conf_customfields.lib.php), particularly the $modulesarray and $triggersarray variables:&lt;br /&gt;
&lt;br /&gt;
=== Configure hooks and main parameters ===&lt;br /&gt;
&lt;br /&gt;
Add every parameters except triggers in the $modulesarray:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$modulesarray = array(&lt;br /&gt;
                      array(&#039;context&#039;=&amp;gt;&#039;invoicecard&#039;, &#039;table_element&#039;=&amp;gt;&#039;facture&#039;, &#039;idvar&#039;=&amp;gt;&#039;facid&#039;, &#039;rights&#039;=&amp;gt;array(&#039;$user-&amp;gt;rights-&amp;gt;facture-&amp;gt;creer&#039;)), // Client Invoice&lt;br /&gt;
                      array(&#039;context&#039;=&amp;gt;&#039;membercard&#039;, &#039;table_element&#039;=&amp;gt;&#039;adherent&#039;, &#039;idvar&#039;=&amp;gt;&#039;rowid&#039;, &#039;rights&#039;=&amp;gt;array(&#039;$user-&amp;gt;rights-&amp;gt;adherent-&amp;gt;creer&#039;), &#039;tabs&#039;=&amp;gt;array(&#039;objecttype&#039;=&amp;gt;&#039;member_admin&#039;, &#039;function&#039;=&amp;gt;&#039;member_admin_prepare_head&#039;, &#039;lib&#039;=&amp;gt;DOL_DOCUMENT_ROOT.&#039;/core/lib/member.lib.php&#039;)), // Members / Adherents&lt;br /&gt;
                     ); // Edit me to add the support of another module - NOTE: Lowercase only!&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are also a lot of others parameters you can give to CustomFields so that the module gets better managed or to add a few other functionnalities, here is a non-exhaustive list:&lt;br /&gt;
* context: name of the module&#039;s context.&lt;br /&gt;
* table_element: name of the sql table that holds the object&#039;s datas.&lt;br /&gt;
* idvar: name of the variable that holds the id of the object in the sql database (by default &#039;id&#039; or &#039;rowid&#039;).&lt;br /&gt;
* rights: prevent the custom fields to be editable unless all the rights set here are enabled (preventing custom fields to be viewed is useless because they won&#039;t show anyway if the page can&#039;t be loaded due to insufficient rights, so this is directly handled by the module).&lt;br /&gt;
* tabs=&amp;gt;array(objecttype, function, lib, tabname, tabtitle): used to embed CustomFields&#039;s admin panel into another module&#039;s admin page.&lt;br /&gt;
&lt;br /&gt;
=== Configure creation triggers ===&lt;br /&gt;
&lt;br /&gt;
Add the triggers in $triggersarray:&lt;br /&gt;
$triggersarray = array(&amp;quot;order_create&amp;quot;=&amp;gt;&amp;quot;commande&amp;quot;,&lt;br /&gt;
                       &amp;quot;yourmodule_triggeraction1&amp;quot;=&amp;gt;&amp;quot;yourmodule_tablename&amp;quot;,&lt;br /&gt;
                       &amp;quot;yourmodule_triggeraction2&amp;quot;=&amp;gt;&amp;quot;yourmodule_tablename&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
Note: generally you will be looking to implement the _CREATE.&lt;br /&gt;
&lt;br /&gt;
Note2: if a triggeraction is not recognized by CustomFields (eg: mymodule_addpayment) then by default CustomFields will assimilate it to a _create action (it will create and save the values for the custom fields, just for a module&#039;s creation page).&lt;br /&gt;
&lt;br /&gt;
Note3: CustomFields also supports a few other triggers like _CLONE, _UPDATE, _MODIFY, but they are not required and may not work depending on how your module works to manage these actions.&lt;br /&gt;
&lt;br /&gt;
=== Configuration troubleshooting ===&lt;br /&gt;
&lt;br /&gt;
* If you cannot see the custom fields appear on your module&#039;s page, this means the hooks are not implemented or configured in CustomFields, please read the related chapters.&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet but there&#039;s no edit button icon: the module implements security but you did not configure it properly in CustomFields, please find the right security in $user-&amp;gt;rights and set it in conf_customfields.lib.php.&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the creation page but are not saved: you forgot to set the trigger in $triggersarray, or the creation trigger does not exist for this module and you have to implement it. Jump to [[Module_CustomFields_Dev#Triggers]].&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet but not on the creation page (or vice versa): A hook is missing on the page where the custom fields do not appear. Jump to [[Module_CustomFields_Dev#Hooks]].&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet with the edit button, but when the edit button is clicked, everything disappear: either the $idvar is not properly set (look at the URL to know if you need to set idvar), or the hook is not properly made ($object is not defined and it&#039;s called by another name, but the hook use $object instead of the correct variable).&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet with the edit button, can be edited but cannot be saved (the data is not saved): same as the previous point: either $idvar or $object bug.&lt;br /&gt;
&lt;br /&gt;
Also you might find useful to &#039;&#039;&#039;[[Module_CustomFields#General_advices_for_troubleshooting|enable php errors]]&#039;&#039;&#039; to better track errors if that&#039;s not already done.&lt;br /&gt;
&lt;br /&gt;
= Example cases =&lt;br /&gt;
&lt;br /&gt;
You can find a compilation of concrete, practical examples on the [[Module_CustomFields_Cases]] wiki page.&lt;br /&gt;
&lt;br /&gt;
= TO DO =&lt;br /&gt;
&lt;br /&gt;
You will find below the public list of features that should be implemented in the future.&lt;br /&gt;
&lt;br /&gt;
If you have an idea about a new feature that isn&#039;t listed below, feel free to add it to the list!&lt;br /&gt;
&lt;br /&gt;
== To Do ==&lt;br /&gt;
Nothing here!&lt;br /&gt;
&lt;br /&gt;
== To Document ==&lt;br /&gt;
* Automatic recopy on cloning for custom fields and products&#039; lines custom fields.&lt;br /&gt;
* new API function: customfields_clone_or_recopy($object, $fromobject, $action2 = null)&lt;br /&gt;
&lt;br /&gt;
== Should do ==&lt;br /&gt;
* Add the possibility to mark a custom field to be shown in tables listings (eg: as a column in products listings) with &amp;quot;order by this column&amp;quot; buttons and stuffs. Will be difficult if the hooks are not yet implemented in listings.&lt;br /&gt;
* Checkbox (multiple checkboxes, one for each possible value) and multiselect (big select and can use SHIFT to multiselect) (store in XML file) (but impossible with constrained fields, can&#039;t store multiple foreign keys ids in one single sql field, but would work for other types like enum)&lt;br /&gt;
use fieldset, input checkbox and label for, eg:&lt;br /&gt;
    &amp;lt;fieldset&amp;gt;&amp;lt;legend&amp;gt;&amp;lt;label for=&amp;quot;cf_productslist&amp;quot;&amp;gt; $langs-&amp;gt;trans(&#039;cf_productslist&#039;)&amp;lt;/label&amp;gt;&amp;lt;/legend&amp;gt;&lt;br /&gt;
    &amp;lt;input type=&amp;quot;checkbox&amp;quot; name=&amp;quot;cf_productslist_choice1&amp;quot; value=&amp;quot;1&amp;quot; id=&amp;quot;cf_productslist_choice1&amp;quot; /&amp;gt;&amp;lt;label for=&amp;quot;cf_productslist_choice1&amp;quot;&amp;gt; $langs-&amp;gt;trans(&#039;cf_productslist_choice1&#039;)&amp;lt;/label&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
    &amp;lt;input type=&amp;quot;checkbox&amp;quot; name=&amp;quot;cf_productslist_choice2&amp;quot; value=&amp;quot;1&amp;quot; id=&amp;quot;cf_productslist_choice2&amp;quot; /&amp;gt;&amp;lt;label for=&amp;quot;cf_productslist_choice2&amp;quot;&amp;gt; $langs-&amp;gt;trans(&#039;cf_productslist_choice2&#039;)&amp;lt;/label&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
    etc...&lt;br /&gt;
    &amp;lt;/fieldset&amp;gt;&lt;br /&gt;
* Add a javascript options values generator for the enum type (a hidden input that would be shown only when DropdownBox type is selected and that would permit to add options by clicking a plus button).&lt;br /&gt;
* Replace triggers in customfields class by hookmanager?&lt;br /&gt;
* AJAX previsualisation of the HTML custom field that will be created.&lt;br /&gt;
* In ODTs, create special tags to directly access day, month or year value of a date type field independently of the rest.&lt;br /&gt;
* New overloading function&#039;s action: &amp;quot;get&amp;quot;, similar to view except that it works not only on Dolibarr&#039;s view but whenever someone tries to access this field&#039;s value.&lt;br /&gt;
* Implementation of custom fields in CSV exports (may need a few new hooks in Dolibarr?).&lt;br /&gt;
* Add Upload and Image field types (this would allow for an easier integration inside ODT and PDF templates than the current workflow of Dolibarr).&lt;br /&gt;
&lt;br /&gt;
== Known bugs ==&lt;br /&gt;
* in product and service modules, if you edit a field, the proposals and other fields below won&#039;t be shown, you need to refresh the page. This problem resides in Dolibarr I think (since we are simply using a hook).&lt;br /&gt;
* ficheinter (Intervention module) custom fields will stay in create mode just after create page is submitted (this is because of the $action that is not standard - $action=edit or something).&lt;br /&gt;
&lt;br /&gt;
== Never/Maybe one day ==&lt;br /&gt;
* Add support for repeatable (predefined) invoices (the way it is currently managed makes it very difficult to manage this without making a big exception, adding specific functions in customfields modules that would not at all will be reusable anywhere else, when customfields has been designed to be as generic as possible to support any module and any version of dolibarr, because it&#039;s managed by a totally different table while it&#039;s still managed by the same module, CustomFields work with the paradigm: one module, one table).&lt;br /&gt;
* Add an AJAX select box for constrained values : when a constrained type is selected and a table is selected, a hidden select box would show up with the list of the fields of this table to choose the values that will be printed as the values for this customfield (eg: for table llx_users you could select the &amp;quot;nom&amp;quot; field and then it would automatically prepend &amp;quot;nom_&amp;quot; to the field&#039;s name).&lt;br /&gt;
* Fine-grained rights management: rights per group, per user, and per field.&lt;br /&gt;
* Replace overloading functions (extend class) by hookmanager if possible (but how to simulate the *full switch? Plus this will force users to make their own modules to make those functions, and to disable/renable their modules everytime they will add a new function...).&lt;br /&gt;
* Refactor the trigger array: merge it with the modulesarray (along with a new way to specify which customfields trigger action a trigger should correspond, eg: &#039;linebill_insert&#039;=&amp;gt;&#039;customfields_create&#039;). But the triggers aren&#039;t necessarily associated with a specific module...&lt;/div&gt;</summary>
		<author><name>Lrq3000</name></author>
	</entry>
	<entry>
		<id>https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Dev&amp;diff=33118</id>
		<title>Module CustomFields Dev</title>
		<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Dev&amp;diff=33118"/>
		<updated>2015-01-22T21:31:59Z</updated>

		<summary type="html">&lt;p&gt;Lrq3000: /* Managing custom fields records */ Add chapter /* Fetching referenced records of a constrained field */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:CustomFields]]&lt;br /&gt;
{{TemplateDocDevEn}}&lt;br /&gt;
{{TemplateModEN}}&lt;br /&gt;
{{BasculeDevUserEn|&lt;br /&gt;
name=CustomFields|&lt;br /&gt;
num=8500|&lt;br /&gt;
devdoc=This page|&lt;br /&gt;
userdoc=[[Module_CustomFields]]|}}&lt;br /&gt;
[[Category:FAQ EN]]&lt;br /&gt;
&lt;br /&gt;
These notes are aimed to developpers who want to extend the functionnalities of the module or want to use it for extreme cases.&lt;br /&gt;
This is not meant to guide implementers on how to implement and configure the module (see the related chapter for that).&lt;br /&gt;
&lt;br /&gt;
If you are an implementer and just want to use CustomFields with its normal functions, you can read the [[Module_CustomFields|user documentation of CustomFields]].&lt;br /&gt;
&lt;br /&gt;
You can also jump directly to [[Module_CustomFields_Cases]] wiki page for a collection of practical examples.&lt;br /&gt;
&lt;br /&gt;
= Principles =&lt;br /&gt;
&lt;br /&gt;
CustomFields is simply a wrapper for SQL databases.&lt;br /&gt;
&lt;br /&gt;
The module is based on SQL to manage the fields and most of the stuff. It uses and understands standard SQL.&lt;br /&gt;
&lt;br /&gt;
The main principles of CustomFields are:&lt;br /&gt;
&lt;br /&gt;
* one SQL column = one Dolibarr field.&lt;br /&gt;
* one SQL type = one method to show and edit the field in Dolibarr.&lt;br /&gt;
* almost everything should be configurable from the configuration file, or admin interface.&lt;br /&gt;
* be independent from Dolibarr&#039;s data structures.&lt;br /&gt;
&lt;br /&gt;
CustomFields sits besides Dolibarr, and does not mix with it, even if in the Dolibarr&#039;s interface it looks like CustomFields is embedded inside Dolibarr, in reality, CustomFields is totally independent.&lt;br /&gt;
&lt;br /&gt;
= Goals =&lt;br /&gt;
&lt;br /&gt;
This module was made to provide an easy way to manage custom sql fields, not only for users and implementers, but also for developpers to use in their own module:&lt;br /&gt;
* by easily adding the support of custom fields for your own module by using this module;&lt;br /&gt;
* by reusing custom fields from another module in your own module;&lt;br /&gt;
* by managing sql fields in your own module by using the CustomFields class (which will take care of everything from printing, editing to PDF/ODT templating).&lt;br /&gt;
&lt;br /&gt;
So this module is not only an auxiliary of other modules, but it can also be the core part of your own module by using it as your SQL query class for fields.&lt;br /&gt;
&lt;br /&gt;
= SQL structure =&lt;br /&gt;
&lt;br /&gt;
A CustomFields table needs to respect three parameters that never change:&lt;br /&gt;
&lt;br /&gt;
* table&#039;s name (format: llx_*dolibarrmodule*_customfields where *dolibarrmodule* = table_element in conf_customfields.lib.php)&lt;br /&gt;
* first column: rowid, which is the primary key for each field&lt;br /&gt;
* second column: fk_*dolibarrmodule* which is the foreign key that links every customfields record to the Dolibarr&#039;s module that is associated (invoice, order, etc...).&lt;br /&gt;
&lt;br /&gt;
Apart from the table&#039;s name and the first two special columns, every other columns are just standard sql columns that you can freely create, edit or delete as you want, from the module&#039;s administration page or from a third-party SQL management tool (such as phpMyAdmin).&lt;br /&gt;
&lt;br /&gt;
You can also create more complex sql fields: constraints, foreign keys, transformations, other sql types, etc...).&lt;br /&gt;
&lt;br /&gt;
Then the module will try to manage the sql fields you manually created the best it can:&lt;br /&gt;
* by printing appropriate input fields in the Dolibarr gui interface.&lt;br /&gt;
* by providing the developper an easy access to these fields in php (eg: $customfields-&amp;gt;cf_myfield or $object-&amp;gt;customfields-&amp;gt;cf_myfield) for any use, including PDF templates and ODT templates, or in any php script (so that you can use these fields in your own module).&lt;br /&gt;
&lt;br /&gt;
= SQL Indexes =&lt;br /&gt;
&lt;br /&gt;
The module automatically adds an index on the primary key and foreign key (first and second columns).&lt;br /&gt;
&lt;br /&gt;
To further optimize the performances of your database, and particularly the customfields tables, you may want to manually add indexes based on usage.&lt;br /&gt;
&lt;br /&gt;
Also, you may add indexes on remote tables used by constrained fields, which could optimize even more the performances since constrained fields have the worst performance drawback for CustomFields.&lt;br /&gt;
&lt;br /&gt;
= SQL Compatibility mode =&lt;br /&gt;
&lt;br /&gt;
The module relies on referential integrity checks and foreign keys of your DBMS to keep the database clean and to provide the constrained fields feature. Referential integrity checks is part of the SQL standards and should be used whenever possible, which always was a main goal for CustomFields (to be respectful of SQL standards).&lt;br /&gt;
&lt;br /&gt;
Unluckily, a few DBMS systems still don&#039;t support this critical functionality of the SQL standard, for example MySQL enables MyISAM engine by default, which doesn&#039;t support this feature.&lt;br /&gt;
&lt;br /&gt;
In these cases, you can either do two things:&lt;br /&gt;
* Switch to an engine which support referential integrity. For example with MySQL, you can enable InnoDB, which support referential integrity checks and foreign keys.&lt;br /&gt;
* Use CustomFields compatibility mode (more infos below).&lt;br /&gt;
&lt;br /&gt;
In the case your database does not support referential integrity checks, CustomFields will automatically switch to the SQL compatibility mode, which will allow you to use CustomFields even on databases where foreign keys are not possible.&lt;br /&gt;
&lt;br /&gt;
In compatibility mode, you will get nearly all CustomFields advanced features such as Constrained fields, but the customfields&#039; tables in the database won&#039;t be cleaned automatically (no delete on cascade, meaning that when you delete an object, the linked custom fields will still be kept in your database), and constrained fields won&#039;t be updated automatically on update.&lt;br /&gt;
&lt;br /&gt;
Nevertheless, these missing cleanup functionalities won&#039;t be seen by your users because they will never be accessible: on deletion of an object, the custom fields won&#039;t ever be accessible again from Dolibarr&#039;s interface, and object&#039;s ids are never changed since Dolibarr also relies on this. Thus in compatibility mode, CustomFields will act just like normally, but your database will be a bit less clean.&lt;br /&gt;
&lt;br /&gt;
= JSON extension for extra options =&lt;br /&gt;
&lt;br /&gt;
== Introduction to JSON in CustomFields ==&lt;br /&gt;
The relational model is very great to store and fetch data with a declarative language, and this is very powerful, but this model also imposes a few limitations, for example the relations must be well formatted and predefined.&lt;br /&gt;
&lt;br /&gt;
That&#039;s why we finally implemented a &#039;&#039;&#039;JSON extension&#039;&#039;&#039; to manage &#039;&#039;&#039;extra options&#039;&#039;&#039; and associate them with custom fields. This allows to add many properties (or columns if you are more used to the relational model) on-the-fly without having to predefine them prior to storing and using them.&lt;br /&gt;
&lt;br /&gt;
This allows to add an undefined number of columns/properties/extra options that will help manage special behaviours for custom fields. For example, it is currently used to allow to change the order of appearance of custom fields, and to store and manage remote table and columns datas for constrained fields on DBMS that do not support Foreign Keys checks.&lt;br /&gt;
&lt;br /&gt;
We chose JSON over XML because the library are simpler and the data footprint smaller, and because CustomFields does not need advanced features of XML like XQuery, data typing and meta structures (at least not for now).&lt;br /&gt;
&lt;br /&gt;
The other advantage is that php objects can directly be encoded into JSON without any preprocessing, and can then as straightly be decoded from JSON back into a php object.&lt;br /&gt;
&lt;br /&gt;
A simple mean to encode and decode back an object into and from a storage place, as well as allow users to easily add/remove extra options were the main requirement, and JSON (+ JSON libraries for PHP) meet this criteria.&lt;br /&gt;
&lt;br /&gt;
However, note that the extra options are now encoded as an associative array, so as to avoid any ambiguities (there&#039;s no associative array in JSON, they are converted to objects).&lt;br /&gt;
&lt;br /&gt;
== Implementation of JSON ==&lt;br /&gt;
The implementation is very simple: a table called &#039;&#039;&#039;llx_customfields_extraoptions&#039;&#039;&#039; is created when you enable the CustomFields module, and this table only contains three columns:&lt;br /&gt;
* table_name (varchar)&lt;br /&gt;
* column_name (varchar)&lt;br /&gt;
* extraoptions (blob)&lt;br /&gt;
&lt;br /&gt;
(table_name, column_name) both forms the primary key of the table, and links to columns (custom fields) of other customfields tables.&lt;br /&gt;
extraoptions is a blob, which allows to store the data as binary (as-is) without encoding (which solves a big problem of JSON which is encoding special and foreign characters, here there is no problem at all).&lt;br /&gt;
&lt;br /&gt;
When fetching custom fields, the module simply JOIN the extraoptions table, which adds an &#039;&#039;&#039;extraoptions&#039;&#039;&#039; column for each custom field in the SQL result. Then it is quite easy to just json_decode() the data returned inside the extraoptions column.&lt;br /&gt;
&lt;br /&gt;
This methodology allows to have all the advantages of a relational database, with the flexibility of a free-schema model:&lt;br /&gt;
* everything is stored inside the database (only one storage place)&lt;br /&gt;
* fixed columns allows for relational joining and optimization&lt;br /&gt;
* thus queries (fetching) are optimised&lt;br /&gt;
* at the same time, the extraoptions are automatically retrieved and associated with every custom field, and whenever needed they can be used&lt;br /&gt;
* and thus we also get a flexible model to &#039;&#039;&#039;add extra (virtual) columns whenever needed&#039;&#039;&#039; without having to alter the relational database schema&lt;br /&gt;
&lt;br /&gt;
Thus no critical information is and should be stored inside the extraoptions column because they would not be query-able, only extra options that are just used to tweak the management of those fields inside the PHP application should be stored. But this is a wonderful way to add non critical informations that still allows to develop more complex features and management for special custom fields.&lt;br /&gt;
&lt;br /&gt;
== CustomFields JSON API ==&lt;br /&gt;
&lt;br /&gt;
=== Storing extra options ===&lt;br /&gt;
&lt;br /&gt;
You can use the &#039;&#039;&#039;setExtra()&#039;&#039;&#039; method to set extra options of a custom field.&lt;br /&gt;
&lt;br /&gt;
Eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$extra = array(); // $extra should always be an associative array, it&#039;s no longer an object&lt;br /&gt;
$extra[&#039;myproperty&#039;] = &#039;Anything I want here&#039;; // set a property/extra option like this&lt;br /&gt;
$extra[&#039;category&#039;][&#039;subcategory&#039;][&#039;prop&#039;] = true; // we can set recursive properties, there&#039;s not any limit to how deep the hierarchy can be&lt;br /&gt;
&lt;br /&gt;
require_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomField($db, &#039;currentmodule&#039;);&lt;br /&gt;
$fieldid = &#039;mycustomfields&#039;; // $fieldid can either be a column_name or an ordinal_position from information_schema.columns&lt;br /&gt;
$result = $customfields-&amp;gt;setExtra($fieldid, $extra); // store the extra options inside the database&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can also use addCustomField() or updateCustomField() with an $extra array, and these functions will also call the setExtra() method.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: the extra options you set are &#039;&#039;&#039;appended&#039;&#039;&#039; to the ones already stored in the database. However, you can also overwrite previous options, just use the same name of property but with a different value (the replacement is recursive, you can do it at any level in the hierarchy).&lt;br /&gt;
&lt;br /&gt;
=== Fetching extra options ===&lt;br /&gt;
&lt;br /&gt;
Extra options are automatically fetched along with a custom field structure, decoded, and stored inside the extra sub-property of any field object, or CustomFields object.&lt;br /&gt;
&lt;br /&gt;
When fetched, extra options are stored as a sub-object for the current field object (which itself is stored as a subobject of the CustomFields object if caching is enabled).&lt;br /&gt;
&lt;br /&gt;
Eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
require_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomField($db, &#039;currentmodule&#039;);&lt;br /&gt;
$fields = fetchAllFieldsStruct(); // fetching fields structure will automatically fetch extra options at the same time. This works with fetchAllFieldsStruct() and fetchFieldStruct()&lt;br /&gt;
&lt;br /&gt;
// Now that you fetched the fields structure, you can just access the extra options sub-properties:&lt;br /&gt;
print_r($fields-&amp;gt;mycustomfield-&amp;gt;extra); // will print all extra options for the field mycustomfield&lt;br /&gt;
print_r($customfields-&amp;gt;fields-&amp;gt;mycustomfield-&amp;gt;extra); // you can also access the same field(s) directly from the CustomFields object if caching is enabled (default behaviour)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Modifying extra options ===&lt;br /&gt;
&lt;br /&gt;
You can just combine the two previous points to edit extra options of a custom field: just load a custom field, edit on-the-fly the extra property with PHP code, and then store it back directly into the database:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
require_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomField($db, &#039;currentmodule&#039;);&lt;br /&gt;
$fields = fetchAllFieldsStruct();&lt;br /&gt;
&lt;br /&gt;
$fields-&amp;gt;mycustomfield-&amp;gt;extra[&#039;newproperty&#039;] = &#039;This is a new property&#039;; // add a new property over the old extra options sub-object&lt;br /&gt;
$fields-&amp;gt;mycustomfield-&amp;gt;extra[&#039;mycategory&#039;] = &#039;Overwrite the old category&#039;; // this will replace the previous value of &#039;mycategory&#039; extra option&lt;br /&gt;
&lt;br /&gt;
$customfields-&amp;gt;setExtra(&#039;mycustomfield&#039;, $fields-&amp;gt;mycustomfield-&amp;gt;extra); // store the modified extra options back into the database for future usage&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= CustomFields API =&lt;br /&gt;
&lt;br /&gt;
== Introduction to CustomFields API ==&lt;br /&gt;
&lt;br /&gt;
This chapter will cover the basics on how to manually and concretely use the CustomFields class to create/update/fetch/manage custom fields.&lt;br /&gt;
&lt;br /&gt;
This will give you a better understanding of how CustomFields works, as well as giving you insights on how to use it for your own applications.&lt;br /&gt;
&lt;br /&gt;
Important note: contrary to the Facade API, the core CustomFields class use columns names without any prefix, so don&#039;t try to use the prefix, it won&#039;t work! For example: if your field is named &#039;mycustomfield&#039; and prefix is &#039;cf_&#039;, when you use the Facade API you should use &#039;$object-&amp;gt;$path-&amp;gt;cf_mycustomfield&#039; but when you use the core CustomFields class directly, $fieldname = &#039;mycustomfield&#039;.&lt;br /&gt;
&lt;br /&gt;
== Prerequisite to use the CustomFields class ==&lt;br /&gt;
&lt;br /&gt;
Before using the CustomFields class, you first must instanciate it. This is a necessary preliminary step before using any of the methods that will be covered inside this chapter, and thus will not be repeated, only shown once here.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Include the CustomFields class (which contains the main API)&lt;br /&gt;
require_once(dirname(__FILE__).&#039;/../class/customfields.class.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Instanciate a CustomFields object&lt;br /&gt;
global $db; // the database handler created by Dolibarr&lt;br /&gt;
$table_element = &#039;currentmodule&#039;; // where $table_element is the database&#039;s table name for the module, without the Dolibarr prefix (eg: for &#039;llx_facture&#039;, table_element is &#039;facture&#039;). You can also find it inside the $object properties (just print_r() it).&lt;br /&gt;
$customfields = new CustomFields($db, $table_element); // instanciate the CustomFields class inside $customfields object&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
CustomFields keeps track of errors that can happen when executing methods and SQL queries inside the &#039;&#039;&#039;error&#039;&#039;&#039; and &#039;&#039;&#039;errors&#039;&#039;&#039; properties, which you can print with the printErrors() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Do something with CustomFields before&lt;br /&gt;
&lt;br /&gt;
if (count($customfields-&amp;gt;errors) &amp;gt; 0) { // If there is at least one error&lt;br /&gt;
    $customfields-&amp;gt;printErrors(); // print errors&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// You can also print your own error message, in two manners:&lt;br /&gt;
&lt;br /&gt;
// 1- submit your own message&lt;br /&gt;
$customfields-&amp;gt;printErrors(&#039;Your own error message here&#039;); // this will print ONLY your error message, using the Dolibarr&#039;s AJAX error dialog (standard way of printing errors)&lt;br /&gt;
&lt;br /&gt;
// 2- append your message in the customfields errors&lt;br /&gt;
$customfields-&amp;gt;errors[] = &#039;Your own error message to be appended with other CustomFields messages&#039;;&lt;br /&gt;
$customfields-&amp;gt;printErrors(); // Here your error message will be appended, thus all error messages will be printed. This allows you for example to add more intelligible informations on a specific error.&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== General purpose SQL methods ==&lt;br /&gt;
&lt;br /&gt;
CustomFields provide an interface to easily execute various common SQL requests, as well as your own custom SQL requests.&lt;br /&gt;
&lt;br /&gt;
* fetchAllTables() fetch a list of all the tables in the database&lt;br /&gt;
* fetchPrimaryField($table) fetch the column_name (and only the column_name!) of the primary field of a table. You can then use fetchFieldStruct()  with the column_name to get any information you want.&lt;br /&gt;
* fetchAny($columns, $table, $where=&#039;&#039;, $orderby=&#039;&#039;, $limitby=&#039;&#039;) allows you to issue any simple SQL query command, for example fetchAny(&#039;*&#039;, &#039;sometable&#039;) is equivalent to SELECT * FROM sometable; (but then everything is managed automatically and result is returned to you)&lt;br /&gt;
* executeSQL($sql, $eventname) to execute any SQL query you want, this will return you a resource (and NOT a processed nuplet like fetchAny!). $eventname can be anything, it&#039;s just for Dolibarr logging facility.&lt;br /&gt;
* executeMultiSQL($sql, $eventname) to execute multiple SQL queries&lt;br /&gt;
* fetchFieldStruct($id=null, $nohide=false, $table=null, $cachepath=null) allows you to fetch the field&#039;s SQL structure of not only custom fields, but of any database&#039;s column using the $table variable. Eg: fetchFieldStruct(&#039;mycolumn&#039;, true, &#039;mytable&#039;);&lt;br /&gt;
* probeTable($table) helps you to check if a table exists in the database&lt;br /&gt;
* fetch_object($resource) will return a PHP object from a $resource, similarly to mysql_fetch_object() but with two advantages: it works on several DBMS (not only mysql), and it always return lowercase properties names (else depending on the configuration of the DBMS, the request can return uppercase or mixed case properties names).&lt;br /&gt;
* escape() and reverse_escape() are similare to mysql_real_escape but is reversible and DBMS-independent (so you can use it with other DBMSes).&lt;br /&gt;
&lt;br /&gt;
== Managing CustomFields structure ==&lt;br /&gt;
&lt;br /&gt;
The first step into CustomFields is to create and manage the structure of a custom field.&lt;br /&gt;
&lt;br /&gt;
=== Creating a custom field definition ===&lt;br /&gt;
&lt;br /&gt;
To create a custom field, you can use the addCustomField() method.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;addCustomField($fieldname, $type, $size, $nulloption, $defaultvalue = null, $constraint = null, $customtype = null, $customdef = null, $customsql = null, $fieldid = null, $extra = null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will take care of forging the (complicated) SQL query with the forgeSQLCustomField() method, and will return you an error if there was a problem.&lt;br /&gt;
&lt;br /&gt;
=== Updating a custom field definition ===&lt;br /&gt;
&lt;br /&gt;
To update a custom field, you can use the updateCustomField() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;&lt;br /&gt;
updateCustomField($fieldid, $fieldname, $type, $size, $nulloption, $defaultvalue, $constraint = null, $customtype = null, $customdef = null, $customsql = null, $extra = null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: the updateCustomField() and addCustomField() methods can both be used since updateCustomField() is in fact an alias for addCustomField(). CustomFields will automatically detect if a custom field already exists, and update it if that&#039;s true, or create it.&lt;br /&gt;
&lt;br /&gt;
Note2: updating a custom field will automatically take care of the constraints and foreign keys on its own, thus you can change the foreign key, or remove it altogether if set to null.&lt;br /&gt;
&lt;br /&gt;
=== Delete a custom field definition ===&lt;br /&gt;
&lt;br /&gt;
You can easily delete a custom field by column_name or by ordinal_position:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;deleteCustomField($id);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: constraints and foreign keys will also be deleted automatically so you shouldn&#039;t encounter any error.&lt;br /&gt;
&lt;br /&gt;
=== Fetch one custom field&#039;s SQL structure ===&lt;br /&gt;
&lt;br /&gt;
To get the SQL structure of a custom field, which can be quite useful to know for example its datatype or if it has a foreign key, or accepts null values, you can use the fetchFieldStruct() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
fields = $customfields-&amp;gt;fetchFieldStruct($id=null, $nohide=false, $table=null, $cachepath=null); // where $id can be a column_name, an ordinal_position from information_schema.columns or null if you want to fetch all the columns at once&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This method will either return null, either a single field object, either an array on field objects.&lt;br /&gt;
&lt;br /&gt;
Note: this function will use caching by default to accelerate future results, but you can disable it, or even change the default cache path. For more informations please read the comments in the source code.&lt;br /&gt;
&lt;br /&gt;
=== Fetch several custom fields&#039; SQL structures ===&lt;br /&gt;
&lt;br /&gt;
Use the fetchAllFieldsStruct() method, which is an alias for fetchFieldStruct():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;fetchAllFieldsStruct($nohide=false, $table=null, $cachepath=null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: this method will always return an array of fields objects, even if only one is found (for more consistency), and this is the main difference with fetchFieldStruct(null).&lt;br /&gt;
&lt;br /&gt;
== Managing custom fields records ==&lt;br /&gt;
&lt;br /&gt;
Now that you know how to create and manage the structure of custom fields, we can proceed on to managing the records.&lt;br /&gt;
&lt;br /&gt;
=== Creating a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Simply use the create() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;create($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
$object must be an object containing as its properties the customfields data.&lt;br /&gt;
&lt;br /&gt;
For example, if you have a field named mycustomfield, your object must have $object-&amp;gt;mycustomfields defined.&lt;br /&gt;
&lt;br /&gt;
This method will store in the database ALL the custom fields that were created/edited and with a valid value (eg: an empty value for a field that does not accept null value is invalid) AT ONCE! So you can modify all the columns of one record at once, but not several records at once.&lt;br /&gt;
&lt;br /&gt;
Concretely, this is very useful with Dolibarr, because modules tends to store everything inside a single object generally called $object, and CustomFields tries to hook onto $object, so that you can just pass $object as is to create() and it will save all the custom fields.&lt;br /&gt;
&lt;br /&gt;
But you can also create an empty object with $object = new stdClass(); and then manually fill the custom fields records with whatever you want, and then call the create() method, this will work as well!&lt;br /&gt;
&lt;br /&gt;
You can also just modify only one column of one record, by just specifying one custom field column_name in your $object. The other custom fields that are not specified won&#039;t be created/modified.&lt;br /&gt;
&lt;br /&gt;
=== Updating a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Use the update() method, which is an alias for the create() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;update($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As noted above, update() is an alias of the create() method, which means that CustomFields will try to detect by itself if a record needs to be updated or created.&lt;br /&gt;
&lt;br /&gt;
=== Deleting a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Use the delete() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$rtncode = $customfields-&amp;gt;delete($id); // where $id is = to fk_moduleid column, NOT rowid&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fetching a custom field&#039;s record ===&lt;br /&gt;
&lt;br /&gt;
Just use the fetch() method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$records = $customfields-&amp;gt;fetch($id=null);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where $id can either be = fk_moduleid (NOT rowid), or null to fetch all the records at once.&lt;br /&gt;
&lt;br /&gt;
This method will either return null, a single record object, or an array of records objects.&lt;br /&gt;
&lt;br /&gt;
=== Fetching all custom fields&#039; records ===&lt;br /&gt;
&lt;br /&gt;
Use the fetchAll() method, which is an alias for fetch():&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$records = $customfields-&amp;gt;fetchAll();&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: this method will always return an array of records object, for more consistency (and this is the main difference with fetch(null) ).&lt;br /&gt;
&lt;br /&gt;
=== Fetching referenced records of a constrained field ===&lt;br /&gt;
&lt;br /&gt;
A constrained custom field points to another database table, and a value for this constrained field is the rowid of a record in the referenced table.&lt;br /&gt;
&lt;br /&gt;
Thus, the principal purpose of a constrained custom field is not the value of the field in itself (it&#039;s simply an integer), but rather the other fields available from the referenced table, the constrained field being in fact just a pointer.&lt;br /&gt;
&lt;br /&gt;
To fetch the remote record from the referenced table, use the fetchReferencedValuesList() of the CustomFields class:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$fkrecord = fetchReferencedValuesList($field, $id=null, $where=null, $allcolumns=false);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
&lt;br /&gt;
* $field is a custom field object (fetched using fetchFieldStruct() or fetchAllFieldsStruct()),&lt;br /&gt;
* $id is the rowid of the record to get, just like fetch() and fetchAll(). Can be null to fetch all remote records (or to use the $where clause).&lt;br /&gt;
* $where is a string without the WHERE clause (eg: &amp;quot;fk_facture=1&amp;quot;) to get finer control. In this case, you can use $id or set $id=null depending on your purpose. This is mainly a private argument to allow for cascade option.&lt;br /&gt;
* $allcolumns if false, it will fetch only the required columns to do the smart value substitution. If true, it will fetch all remote columns (useful for ODT substitution). If an array of strings, each string will be a column to fetch from the remote database.&lt;br /&gt;
&lt;br /&gt;
Note that this function is NOT recursive (it will fetch the records from the referenced table, and that&#039;s all, it won&#039;t fetch the custom fields of the referenced table nor recursively fetch the remote constrained custom fields). To do recursive fetching, see in the Facade API.&lt;br /&gt;
&lt;br /&gt;
== Input forms for custom fields ==&lt;br /&gt;
&lt;br /&gt;
The API provides some neat ways to automatically manage the input forms for users to input new values, with the best adapted HTML form based on the custom field&#039;s data type.&lt;br /&gt;
&lt;br /&gt;
The following methods will print input fields that are best adapted to the SQL data type of your custom fields as well as figure out the possible values and even format them (for example: a not null enum will be printed as a HTML select list without an empty option; fetch automatically remote columns for constrained fields and show them in a select).&lt;br /&gt;
&lt;br /&gt;
So you don&#039;t have to bother how your data will be presented to the user nor how it will be processed.&lt;br /&gt;
&lt;br /&gt;
=== Printing an input form adapted to the field&#039;s data type ===&lt;br /&gt;
&lt;br /&gt;
showInputField() is a sort of converter customfieldstructure -&amp;gt; HTML input field.&lt;br /&gt;
&lt;br /&gt;
Use the &#039;&#039;&#039;showInputField()&#039;&#039;&#039; method to print the right input form for a custom field.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;showInputField($field, $currentvalue=null, $moreparam=&#039;&#039;, $ajax_php_callback=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
showInputField() takes 4 parameters:&lt;br /&gt;
&lt;br /&gt;
* $field is a CustomFields structure object (eg: returned by fetchFieldStruct()) and is necessary to detect the right datatype and the possible values.&lt;br /&gt;
* $currentvalue is the current value if you want to preselect a value (for example if the user already defined a value, you can reload it here). This is optional, in case it is not defined, either a null/empty value will be chosen, or either the default value defined when creating the custom field.&lt;br /&gt;
* $moreparam is used to add more HTML attributes, if you want for example to add some css styling or whatever you want.&lt;br /&gt;
* $ajax_php_callback is optional and allows to specify the relative url to the php callback script. If specified, an AJAX script will be automatically attached to this field (see AJAX-PHP callback below).&lt;br /&gt;
&lt;br /&gt;
showInputField() only prints ONE HTML input field, thus you have to enclose it/them by your own HTML form.&lt;br /&gt;
&lt;br /&gt;
eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print &#039;&amp;lt;form method=&amp;quot;get&amp;quot; action=&amp;quot;?&amp;quot;&amp;gt;&#039;;&lt;br /&gt;
print &#039;&amp;lt;input type=&amp;quot;hidden&amp;quot; name=&amp;quot;customtoken&amp;quot; value=&amp;quot;ABCDEFGH&amp;quot; /&amp;gt;&#039;; // example of a custom hidden HTML input field, that can be used to pass a token for example&lt;br /&gt;
// Loop over each custom fields (you have to load them before with $customfields-&amp;gt;fetchAllFieldsStruct()&lt;br /&gt;
foreach ($customfields-&amp;gt;fields as $field) {&lt;br /&gt;
    print $customfields-&amp;gt;showInputField($field, $currentvalue=null, $moreparam=&#039;&#039;);&lt;br /&gt;
}&lt;br /&gt;
print &#039;&amp;lt;/form&amp;gt;&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can also store the resulting HTML input field and process it by a regexp for example:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$myhtmlfield = $customfields-&amp;gt;showInputField($field, $currentvalue=null, $moreparam=&#039;&#039;);&lt;br /&gt;
preg_replace($myhtmlfield, ..., ...);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note: for constrained fields, if the name follows the convention of &#039;&#039;&#039;Smart Value Substitution&#039;&#039;&#039; (ie: remote columns names are included in the custom field&#039;s name), showInputField() will &#039;&#039;&#039;automatically fetch the remote values, format them, and present them&#039;&#039;&#039; in a HTML select.&lt;br /&gt;
&lt;br /&gt;
=== Printing an input form for a custom field ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;showInputForm()&#039;&#039;&#039; is a function used to print a single (and only one!) custom field with an adapted HTML input field, but enclosed by a HTML form. This is used for example when you want to allow to edit a single custom field but not the others.&lt;br /&gt;
&lt;br /&gt;
showInputForm() is in fact a wrapper for showInputField(), thus it will call showInputField() and simply enclose it by a form.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;showInputForm($id, $field, $currentvalue=null, $idvar=&#039;id&#039;, $page=null, $moreparam=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== AJAX-PHP callback ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;showInputFieldAjax()&#039;&#039;&#039; will output an AJAX script (using jQuery) to send and receive data to and from a specified php script. The AJAX script will automatically manage HTML form input&#039;s updating as long as the received data follows some convention.&lt;br /&gt;
&lt;br /&gt;
An AJAX script can be attached to any field (including non-CustomFields) by using the method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;showInputFieldAjax($id, $php_callback_url, $on_func=&amp;quot;change&amp;quot;, $request_type=&amp;quot;post&amp;quot;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
* $id is the HTML id of the field you want to attach the AJAX to.&lt;br /&gt;
* $phpcallback the relative path from Dolibarr&#039;s htdocs root folder to the php script that will receive and send back data.&lt;br /&gt;
* $on_func is the Javascript or jQuery event that should trigger the AJAX script (by default on change).&lt;br /&gt;
* $request_type is the HTML query type that will be used to send the data to the PHP callback (either &amp;quot;get&amp;quot; or &amp;quot;post&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
The AJAX script will automatically manage sending/receiving of data:&lt;br /&gt;
&lt;br /&gt;
* automatically send all form&#039;s inputs values, as well as the current calling field&#039;s name and value. The sending happens on change by default, but another event can be set using the $on_event argument. Data will be sent using standard GET or POST.&lt;br /&gt;
* Upon reception of data from the php script, the AJAX script will automatically parse the data and update the HTML fields.&lt;br /&gt;
&lt;br /&gt;
The PHP callback script must send back to AJAX the data in JSON encoded format, and with with a specific format:&lt;br /&gt;
&lt;br /&gt;
* the data must be an associative array, where each entry&#039;s key is the name (not ID!) of an HTML field to update.&lt;br /&gt;
* each entry&#039;s value is an associative array, where each sub-entry&#039;s key is the action to do. Available actions: &#039;options&#039;, &#039;value&#039;, &#039;html&#039;, &#039;alert&#039;.&lt;br /&gt;
* each sub-entry&#039;s value is the value for this action and HTML field.&lt;br /&gt;
&lt;br /&gt;
Thus, this approach allows to generically support a wide range of updating actions, managed automatically.&lt;br /&gt;
&lt;br /&gt;
You, the implementer, have just to edit the PHP callback script and return an associative array defining what action you want for each field you want to update. This is exactly what has been implemented in the Custom AJAX Functions library, which simplifies even more your job by managing automatically the step described here (generating AJAX and formatting data), so that you can focus on &#039;&#039;&#039;making the data&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Printing custom fields in documents ==&lt;br /&gt;
&lt;br /&gt;
The API can also automatically manage the printing and formatting of custom fields in your ODT, PDF and HTML documents.&lt;br /&gt;
&lt;br /&gt;
=== Printing in HTML ===&lt;br /&gt;
&lt;br /&gt;
You can either just print the value as-is, or pass them through the printField() or simpleprintField() function to format them (very useful for constrained fields) - see below.&lt;br /&gt;
&lt;br /&gt;
=== Printing in documents ===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;printField()&#039;&#039;&#039; is used to properly print and translate a custom field&#039;s record&#039;s value. Generally, you can just print the custom field&#039;s value as-is, but in a few cases (eg for boolean types, or constrained fields), using the printField() method will more properly print the value.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;printField($field, $value, $outputlangs=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
* $field is the custom field&#039;s SQL structure as an object (eg: returned by fetchFieldStruct()).&lt;br /&gt;
* $value is the custom field&#039;s value for the current record (eg: $customfields-&amp;gt;fields-&amp;gt;myfield).&lt;br /&gt;
* $outputlangs is the Translate object for the target language.&lt;br /&gt;
&lt;br /&gt;
For example, with a constrained field with Smart Value Substitution, instead of printing the id like 1, it will print the full name of user like FirstName Name.&lt;br /&gt;
&lt;br /&gt;
This method will also translate a value if a translation is found in the language files.&lt;br /&gt;
&lt;br /&gt;
Another method &#039;&#039;&#039;printFieldPDF()&#039;&#039;&#039; can be used to properly print a value for a PDF (properly encoding the characters).&lt;br /&gt;
&lt;br /&gt;
=== Simple printing in documents ===&lt;br /&gt;
&lt;br /&gt;
If you don&#039;t want to have to manually fetch the structure of every custom field whose you want to print the value, you can then use a simplifier method called &#039;&#039;&#039;simpleprintField()&#039;&#039;&#039; which will take as input a custom field name instead of a custom field SQL structure object.&lt;br /&gt;
&lt;br /&gt;
This method is just a simplifier (Facade) wrapper for the printField() method, and thus will have the same advantages (fetch constrained fields, values translation, etc.).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
print $customfields-&amp;gt;simpleprintField($fieldname, $value, $outputlangs=&#039;&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Where:&lt;br /&gt;
* $fieldname is the custom field&#039;s name (eg: &amp;quot;myfield&amp;quot;).&lt;br /&gt;
* $value is the custom field&#039;s value for the current record (eg: $customfields-&amp;gt;fields-&amp;gt;myfield).&lt;br /&gt;
* $outputlangs is the Translate object for the target language.&lt;br /&gt;
&lt;br /&gt;
Another method &#039;&#039;&#039;simpleprintFieldPDF()&#039;&#039;&#039; can be used to properly print a value for a PDF (properly encoding the characters).&lt;br /&gt;
&lt;br /&gt;
=== Find the label ===&lt;br /&gt;
&lt;br /&gt;
Finding the label of a custom field might not be as simple as it seems. First, values of properties objects may be easily used, but their name (key) is a worst job. Secondly, custom fields&#039; labels can be translated, and the following functions are expressly made for that purpose. Thirdly, for PDF and some other documents you have to encode the characters if you want them to properly be drawn.&lt;br /&gt;
&lt;br /&gt;
So if you want to print a custom field&#039;s label with translation (multilingual) support, just use the &#039;&#039;&#039;findLabel()&#039;&#039;&#039; method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$translatedfieldname = $customfields-&amp;gt;findLabel($fieldname, $outputlangs = &#039;&#039;); // where $fieldname is just a custom field label (eg: cf_mycustomfield or mycustomfield, both will work) and $outputlangs is the target language object (see the Translate class native of Dolibarr)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
As for the other printing methods, you also have the &#039;&#039;&#039;findLabelPDF()&#039;&#039;&#039; wrapper method to easily print labels that with characters encoded for PDF.&lt;br /&gt;
&lt;br /&gt;
= Facade API =&lt;br /&gt;
&lt;br /&gt;
The Facade API was made to ease the usage of the CustomFields class with the Dolibarr system. It provides a neat way to do many operations with single commands, and will automatically manage the loading of the CustomFields class, and its usage with many optimizations.&lt;br /&gt;
&lt;br /&gt;
== customfields_fill_object() ==&lt;br /&gt;
&lt;br /&gt;
You can load the Facade API and then use the &#039;&#039;&#039;customfields_fill_object()&#039;&#039;&#039; to automatically fetch the fields structures and the records, and also automatically link to remote fields when a constrained field is found. This is an all-in-one solution to easily fetch CustomFields datas, and is also optimized to fully use caching and other special properties of the CustomFields class.&lt;br /&gt;
&lt;br /&gt;
Concretely, you just have to include the Facade API, then pass the customfields_fill_object() with a Dolibarr $object, and the Facade API will return a CustomFields object with appended the custom fields records and also the custom fields&#039; SQL structures.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$customfields = customfields_fill_object($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* $object will then include the custom fields records associated with this object (eg: $object-&amp;gt;customfields-&amp;gt;cf_yourfield).&lt;br /&gt;
* $customfields will be a CustomFields object containing the fields structures (eg: $customfields-&amp;gt;fields-&amp;gt;yourfield).&lt;br /&gt;
&lt;br /&gt;
You can also call any CustomFields function if you need to using the $customfields object, eg: $customfields-&amp;gt;simpleprintField($object-&amp;gt;customfields-&amp;gt;cf_yourfield);&lt;br /&gt;
&lt;br /&gt;
Note: $object can also be a dummy object containing only two properties that are required: $object-&amp;gt;id and $object-&amp;gt;table_element.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will now describe the full specification of this function:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$customfields = customfields_fill_object(&amp;amp;$object,$fromobject = null, $outputlangs = null, $prefix = null,$pdfformat = false);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* $object is the object where the customfields will be saved&lt;br /&gt;
* $fromobject is the object from where the customfields will be fetched (this can be a different module than $object, or can be null to be the same as $object)&lt;br /&gt;
* $outputlangs is the target language for translation of fields value (if empty, the default current global translation object will be used)&lt;br /&gt;
* $prefix allows you to save the custom fields in a sub-object. By default, when $prefix=null, custom fields are saved in the path $object-&amp;gt;customfields, but when you specify a prefix, custom fields will be saved inside $object-&amp;gt;customfields-&amp;gt;$prefix. This may be useful if you want to store multiple object&#039;s custom fields or records in the same $object.&lt;br /&gt;
* $pdfformat defines if you want a special formatting for the values: if null, there&#039;s no formatting, if false, there is a simple formatting (eg: translation of fields&#039; values), if true then the formatting will be adapted for PDF templating.&lt;br /&gt;
&lt;br /&gt;
For more informations on pratical usages of this function, please [[Module_CustomFields#Implementing_in_php_code_.28dolibarr_core_modules_or_your_own_module.29|read the user documentation on how to implement CustomFields in php code]] or [[Module_CustomFields_Cases#Linking_Dolibarr_objects_from_two_different_modules|read an example case of linking two objects of different modules together]].&lt;br /&gt;
&lt;br /&gt;
== customfields_fill_object_lines() ==&lt;br /&gt;
&lt;br /&gt;
Similar to customfields_fill_object() for normal custom fields, there is also a &#039;&#039;&#039;customfields_fill_object_lines()&#039;&#039;&#039; to fetch custom fields associated with lines of products/services. This function is also optimized and it is strongly advised to use it and to not try to fetch manually this kind of custom fields (ie: associated with lines of products/services rather than objects), because there are a lot of optimizations going on behind the scene and that are very hard to do manually.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields for lines of products/services (you can then access them by doing $object-&amp;gt;customfields-&amp;gt;lines-&amp;gt;$lineid-&amp;gt;cf_yourfield)&lt;br /&gt;
$customfields = customfields_fill_object_lines($object);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Requirements: $object can be a dummy object but must contain at least 2 properties: $object-&amp;gt;id and $object-&amp;gt;table_element_line.&lt;br /&gt;
&lt;br /&gt;
For more informations on pratical usages of this function, please [[Module_CustomFields#Implementing_in_php_code_.28dolibarr_core_modules_or_your_own_module.29|read the user documentation on how to implement CustomFields in php code]].&lt;br /&gt;
&lt;br /&gt;
= Architecture =&lt;br /&gt;
Here is a full list of the CustomFields packaged files with a short description (for a more in-depth view just crawl the source files, they are full of comments):&lt;br /&gt;
&lt;br /&gt;
== Core files ==&lt;br /&gt;
files that are necessary for the CustomFields to work, they contains the core functions&lt;br /&gt;
&lt;br /&gt;
* /customfields/admin/customfields.php --- Administrator&#039;s configuration panel : this is where you create and manage the custom fields definitions&lt;br /&gt;
* /customfields/class/actions_customfields.class.php --- Hooks class : used to hook into Dolibarr core modules without altering any core file (can be used to hook into your own modules too). Also used to preprocess the parameters before calling the printforms library.&lt;br /&gt;
* /customfields/class/customfields.class.php --- Core class : every database action is made here in this class. You can find some printing functions because they are very generic.&lt;br /&gt;
* /customfields/conf/conf_customfields.lib.php --- Configuration file : contains the main configurable variables to adapt CustomFields to your needs or to expand its support to other modules and more native sql types.&lt;br /&gt;
* /customfields/conf/conf_customfields_func.lib.php --- Auxiliary functions library to read and manage config variables&lt;br /&gt;
* /customfields/core/modules/modCustomFields.class.php --- Dolibarr&#039;s module definition file : this is a core file necessary for Dolibarr to recognize the module and to declare the hooks to Dolibarr (but it does not store anything else than meta-informations).&lt;br /&gt;
* /customfields/core/substitutions/functions_customfields.lib.php --- CustomFields substitution class for ODT generation : necessary to support customfields tags in ODT files&lt;br /&gt;
* /customfields/core/triggers/interface_50_modCustomFields_SaveFields.class.php --- Core triggers file : this is where the actions on records are managed. This is an interface between other modules and CustomFields management. You should not modify this file but you should add your triggers in $triggersarray inside conf_customfields.lib.php, but if you really have a special need and know what you do, you can edit this file to directly add your own triggers. Also, if CustomFields are shown in the forms but cannot be modified nor saved, then probably this trigger file is not detected by Dolibarr (maybe you can try to lower the number 50 to raise the priority?).&lt;br /&gt;
* /customfields/fields/customfields_fields_extend.lib.php.example --- Overloading functions library with users functions: this is where implementers can use their own php code to manage the custom fields they want (one function per action and per field).&lt;br /&gt;
* /customfields/fields/customfields_fields_ajax_custom.lib.php.example --- Custom AJAX functions library with users functions: this is where implementers can use their own php code to manage the custom fields cascading and AJAX calls, similarly to the Overloading functions library (one function per action and per field).&lt;br /&gt;
* /customfields/langs/code_CODE/customfields.lang --- Core language file : this is where you can translate the admin config panel (data types names, labels, descriptions, etc.)&lt;br /&gt;
* /customfields/langs/code_CODE/customfields-user.lang --- User defined language file : this is where you can store the labels and values of your custom fields (see the related chapter)&lt;br /&gt;
* /customfields/lib/customfields_aux.lib.php --- Simplifier library to easily use CustomFields class to populate an $object with custom fields datas (Facade design pattern). This is what you should use for most of your needs.&lt;br /&gt;
* /customfields/lib/customfields_printforms.lib.php --- Core printing library for records : contains only printing functions, there&#039;s not really any core functions inside but it allows to manage the printing of the custom fields records and their editing.&lt;br /&gt;
* /customfields/lib/customfields_ajax_wrapper.lib.php --- AJAX wrapper library to automatically manage cascading at objects creation. This isn&#039;t really necessary for CustomFields to function, this is more an addition to allow dynamical AJAX updating, but CustomFields core functionalities can work without this library.&lt;br /&gt;
* /customfields/sql/*.sql --- Unused (the tables are created directly via a function in the customfields.class.php) - but the sql files are still valid, they were used as squeletton in the first designs.&lt;br /&gt;
&lt;br /&gt;
== PDFTest module ==&lt;br /&gt;
optional module to append a page listing all custom fields values on newly generated PDF documents. To be used to test if custom fields are working correctly before making one&#039;s own template.&lt;br /&gt;
&lt;br /&gt;
* /customfieldspdftest/class/actions_customfieldspdftest.class.php --- Main functions library and hooks class, to hook onto pdf generation. Contains the PDFTest functions.&lt;br /&gt;
* /customfieldspdftest/core/modules/modCustomFieldsPDFTest.class.php --- Module descriptor for Dolibarr&lt;br /&gt;
&lt;br /&gt;
== Runtime outline ==&lt;br /&gt;
&lt;br /&gt;
Here is an outline of how CustomFields works: what functions will be called and why:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At admin page:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* No external function call (apart for some icons or needed admin functions like for tabs), everything that is done are sql queries via CustomFields class (to fetch and modify the sql columns in the customfields tables).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At printing:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Module&#039;s hook formObjectOptions calls Dolibarr&#039;s hookmanager&lt;br /&gt;
* Hookmanager calls CustomFields /class/actions_customfields.class.php, function formObjectOptions() of CustomFields&lt;br /&gt;
* Preprocess a few parameters (mainly the $action) and calls customfields_print_forms()&lt;br /&gt;
* customfields_print_forms() preprocess all the hook&#039;s parameters and assimilate them with the CustomFields&#039;s configuration parameters (there&#039;s no connection to the database at this stage, only parameters preprocessing), then calls /lib/customfields_printforms.lib.php, functions customfields_print_creation_form() or customfields_print_datasheet_form&lt;br /&gt;
* both customfields_print_creation_form() lookup the database using CustomFields class and prints empty custom fields; customfields_print_datasheet_form() lookup the database using CustomFields class, fetch the values of the custom fields and prints them;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At edition:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Same steps as &#039;&#039;&#039;At printing&#039;&#039;&#039;&lt;br /&gt;
* then customfields_print_datasheet_form() process the custom field being edited differently than the others: if editing, it prints the html input field (to enter a new value), or if saving it just saves the data into the database by calling the CUSTOMFIELDS_MODIFY trigger and then continue to print the other fields normally.&lt;br /&gt;
* CUSTOMFIELDS_MODIFY trigger calls $customfields-&amp;gt;update($newrecord);&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At creation (saving):&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Module&#039;s creation trigger MYMODULE_CREATE calls Dolibarr&#039;s run_trigger()&lt;br /&gt;
* Dolibarr run_trigger() calls CustomFields trigger file /core/trigger/interface_50_modCustomFields_SaveFields.class.php, function run_trigger()&lt;br /&gt;
* CustomFields&#039;s run_trigger() assimilate the given parameters with the CustomFields&#039;s config parameters, and if the trigger is found in $triggersarray, then the appropriate action is taken (by default: _CREATE or another standard action if recognized), if not in $triggersarray, the function will still try to automatically detect the appropriate action by detecting MYMODULE_ALISTOFACTIONS with a regular expression.&lt;br /&gt;
* The appropriate action is then done (creation in this case) by calling $customfields-&amp;gt;create($record).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At edition or creation of product lines:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Similar to &#039;&#039;&#039;At edition&#039;&#039;&#039; but with hooks: formCreateProductOptions and formEditProductOptions.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At ODT document generation and substitution:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Module&#039;s ODT generator calls for Dolibarr&#039;s function complete_substitution_array(), which then calls for CustomFields substitution file /core/substitution/functions_customfields.lib.php, function customfields_completesubstitutionarray()&lt;br /&gt;
* CustomFields&#039;s function customfields_completesubstitutionarray() calls the simplifier library customfields_aux.lib.php, function customfields_fill_object() and make the substitution inside the $substitutionarray.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;- At ODT document generation and substitution for product lines:&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* Same as the previous point but this function is called for every product lines, and thus the data is cached. Also the simplifier function is here called customfields_fill_object_lines(), and the CustomFields&#039;s substitution function called customfields_completesubstitutionarray_lines().&lt;br /&gt;
&lt;br /&gt;
= How to add the support of a new module =&lt;br /&gt;
&lt;br /&gt;
There are two cases: either the module already implements hooks, and you just have to configure CustomFields to support it, or either the module does not implements hooks nor triggers.&lt;br /&gt;
&lt;br /&gt;
This chapter will guide you through the steps to make any module supported by CustomFields (be it a Dolibarr core module or your own module).&lt;br /&gt;
&lt;br /&gt;
== Preliminary test ==&lt;br /&gt;
&lt;br /&gt;
This preliminary test will allow you to know at a glance how much work needs to be done in order to make your module supported by CustomFields, and it will also give you all the config parameters if they are available.&lt;br /&gt;
&lt;br /&gt;
Important note: this test will only give data about hooks, which is the main part to make CustomFields work for your module. But for triggers, there is no way to test, so you will have to check the triggers manually (either by testing if the custom fields are saved at creation page, or by looking at the source code of the objects classes).&lt;br /&gt;
&lt;br /&gt;
Edit the php script of the module (check the URL when you access the module&#039;s datasheet or creation page - attention both are two different pages, so you have to do this twice), then put this at the very &#039;&#039;&#039;end&#039;&#039;&#039; of the php script:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// CustomFields debuglines to show config parameters for current module&lt;br /&gt;
print(&amp;quot;&amp;lt;pre&amp;gt;&amp;quot;); // html nice output&lt;br /&gt;
print(&amp;quot;table_element: {$object-&amp;gt;table_element} - table_element_line: {$object-&amp;gt;table_element_line} - contexts: &amp;quot;);&lt;br /&gt;
print_r($hookmanager-&amp;gt;contextarray);&lt;br /&gt;
print(&amp;quot; - conf hooks_modules: &amp;quot;);&lt;br /&gt;
print_r($conf-&amp;gt;hooks_modules);&lt;br /&gt;
print(&amp;quot; - hooks attached: &amp;quot;);&lt;br /&gt;
print_r($hookmanager-&amp;gt;hooks);&lt;br /&gt;
print(&amp;quot; - user rights: &amp;quot;);&lt;br /&gt;
print_r($user-&amp;gt;rights);&lt;br /&gt;
print(&amp;quot;&amp;lt;/pre&amp;gt;&amp;quot;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now reload the page, and scroll at the very bottom. You should see some informations about the module.&lt;br /&gt;
&lt;br /&gt;
Depending on the data available, you will know what you have to do:&lt;br /&gt;
&lt;br /&gt;
* If all the datas is available, then it&#039;s ok, jump to [[Module_CustomFields_Dev#Configuring_CustomFields_to_support_the_module]] and copy these datas to the parameters of conf_customfields.lib.php&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;table_element&#039;&#039;&#039;: $object is not defined, try to find how is called the main object variable and replace $object by this variable in the debug code given above (eg: in Agenda, the object variable is called $agenda and $agendacomm).&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;table_element_line&#039;&#039;&#039;: either there&#039;s no products lines for this module, either the main object variable is not called $object (see the previous point). One last case is that the object is not standard and does not store table_element_line (the name of the table that stores its products lines), in which case you have to manually add it in the class (eg: $this-&amp;gt;table_element_line = &#039;something&#039;).&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;contexts&#039;&#039;&#039;: the hookmanager is not defined, and probably there&#039;s no hook at all in this module, so you&#039;ll have to implement them by yourself. Jump to [[Module_CustomFields_Dev#Hooks]].&lt;br /&gt;
&lt;br /&gt;
* If there is a list of &#039;&#039;&#039;contexts&#039;&#039;&#039; but the module cannot be found in the list: the hookmanager is defined but the module has not defined a context, you still have to implement the hooks. Jump to [[Module_CustomFields_Dev#Hooks]].&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;conf hooks_modules&#039;&#039;&#039;: this variable is not necessary, empty or not it doesn&#039;t matter. But if you see customfields and/or customfieldspdftest inside the list, it is a confirmation that CustomFields indeed is hooking the module correctly.&lt;br /&gt;
&lt;br /&gt;
* If there&#039;s no &#039;&#039;&#039;hooks attached&#039;&#039;&#039;, or CustomFields cannot be found in the list: CustomFields has not been properly configured to hook to this module, or you forgot to disable then re enable CustomFields in order to update the CustomFields&#039;s hooks. Jump to [[Module_CustomFields_Dev#Configuring_CustomFields_to_support_the_module]].&lt;br /&gt;
&lt;br /&gt;
* If there is no &#039;&#039;&#039;$user-&amp;gt;rights&#039;&#039;&#039;: there is either no standard security implemented in this module, or either the variable is called with another name. If there is no security, you don&#039;t need to configure it in CustomFields, but if there is a security, you need to find how the variable is called (like $object can get another name sometimes).&lt;br /&gt;
&lt;br /&gt;
* If there is &#039;&#039;&#039;$user-&amp;gt;rights&#039;&#039;&#039; but the module can&#039;t be found in the list: either the name for the rights is different (this often happens, the rights array is not yet standardized), or either the rights are hardcoded into the module&#039;s code (rights should be announced to the $user-&amp;gt;rights array, but sometimes it happens that the dev forgot to do so, and thus the right is entirely hardcoded and you can&#039;t find it in the dynamic rights array). In any of these two cases, you will have to manually read the module&#039;s code to find the right. Also, maybe there is no security implemented, but since $user-&amp;gt;rights is declared this is less probable.&lt;br /&gt;
&lt;br /&gt;
Here is an example of output on a correctly hooked module (invoice):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
table_element: facture - table_element_line: facturedet - contexts: Array&lt;br /&gt;
(&lt;br /&gt;
    [0] =&amp;gt; searchform&lt;br /&gt;
    [1] =&amp;gt; leftblock&lt;br /&gt;
    [2] =&amp;gt; toprightmenu&lt;br /&gt;
    [3] =&amp;gt; invoicecard&lt;br /&gt;
)&lt;br /&gt;
 - conf hooks_modules: Array&lt;br /&gt;
(&lt;br /&gt;
    [customfields] =&amp;gt; invoicecard:propalcard:productcard:ordercard:thirdpartycard:contactcard:ordersuppliercard:invoicesuppliercard:membercard:actioncard:projectcard:projecttaskcard:contractcard:interventioncard:doncard:tripsandexpensescard:taxvatcard:expeditioncard:invoicecard:propalcard:ordercard:ordersuppliercard:invoicesuppliercard&lt;br /&gt;
    [customfieldspdftest] =&amp;gt; Array&lt;br /&gt;
        (&lt;br /&gt;
            [0] =&amp;gt; pdfgeneration&lt;br /&gt;
            [1] =&amp;gt; ordersuppliercard&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
)&lt;br /&gt;
 - hooks attached: Array&lt;br /&gt;
(&lt;br /&gt;
    [invoicecard] =&amp;gt; Array&lt;br /&gt;
        (&lt;br /&gt;
            [customfields] =&amp;gt; ActionsCustomFields Object&lt;br /&gt;
                (&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
)&lt;br /&gt;
 - user rights: stdClass Object&lt;br /&gt;
(&lt;br /&gt;
    [user] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [user] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [lire] =&amp;gt; 1&lt;br /&gt;
                    [creer] =&amp;gt; 1&lt;br /&gt;
                    [password] =&amp;gt; 1&lt;br /&gt;
                    [supprimer] =&amp;gt; 1&lt;br /&gt;
                    [export] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [self] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [creer] =&amp;gt; 1&lt;br /&gt;
                    [password] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [user_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [readperms] =&amp;gt; 1&lt;br /&gt;
                    [write] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [self_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [readperms] =&amp;gt; 1&lt;br /&gt;
                    [writeperms] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [group_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [read] =&amp;gt; 1&lt;br /&gt;
                    [readperms] =&amp;gt; 1&lt;br /&gt;
                    [write] =&amp;gt; 1&lt;br /&gt;
                    [delete] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    [facture] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [lire] =&amp;gt; 1&lt;br /&gt;
            [creer] =&amp;gt; 1&lt;br /&gt;
            [invoice_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [unvalidate] =&amp;gt; 1&lt;br /&gt;
                    [send] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [valider] =&amp;gt; 1&lt;br /&gt;
            [paiement] =&amp;gt; 1&lt;br /&gt;
            [supprimer] =&amp;gt; 1&lt;br /&gt;
            [facture] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [export] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    [propale] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [lire] =&amp;gt; 1&lt;br /&gt;
            [creer] =&amp;gt; 1&lt;br /&gt;
            [valider] =&amp;gt; 1&lt;br /&gt;
            [propal_advance] =&amp;gt; stdClass Object&lt;br /&gt;
                (&lt;br /&gt;
                    [send] =&amp;gt; 1&lt;br /&gt;
                )&lt;br /&gt;
&lt;br /&gt;
            [cloturer] =&amp;gt; 1&lt;br /&gt;
            [supprimer] =&amp;gt; 1&lt;br /&gt;
            [export] =&amp;gt; 1&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    [produit] =&amp;gt; stdClass Object&lt;br /&gt;
        (&lt;br /&gt;
            [lire] =&amp;gt; 1&lt;br /&gt;
            [creer] =&amp;gt; 1&lt;br /&gt;
            [supprimer] =&amp;gt; 1&lt;br /&gt;
            [export] =&amp;gt; 1&lt;br /&gt;
        )&lt;br /&gt;
&lt;br /&gt;
    etc... a lot of other rights...&lt;br /&gt;
&lt;br /&gt;
)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When you are finished with implementing the support of CustomFields in the module, you should delete these debug lines.&lt;br /&gt;
&lt;br /&gt;
== Implementing the support of CustomFields in the module ==&lt;br /&gt;
&lt;br /&gt;
If there is a module you want to implement the support of CustomFields, but does not yet has the required components, here is a guide on how to implement these components.&lt;br /&gt;
&lt;br /&gt;
=== Hooks ===&lt;br /&gt;
&lt;br /&gt;
Implementing hooks will allow custom fields to appear on your module&#039;s page. They will also be editable and they will save the data. For creation, you need to use a trigger (see the subchapter below).&lt;br /&gt;
&lt;br /&gt;
First you should be familiar with the way that hooks are implemented in modules. If you don&#039;t, please first [[Hooks_system#Implementation|read the hooks documentation]].&lt;br /&gt;
&lt;br /&gt;
An example case of implementation of hooks into a Dolibarr core file can be found in the [[Module_CustomFields_Cases|CustomFields Example Cases wiki page]].&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Datasheet hook&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
To implement CustomFields in your module, you must initialize the hook manager, then use the following hook context:&lt;br /&gt;
* formObjectOptions&lt;br /&gt;
&lt;br /&gt;
This hook must be placed everywhere in your code where you want custom fields to appear (eg: on the creation page and datasheet view page).&lt;br /&gt;
&lt;br /&gt;
Note: Generally, you will have to place the hook just before the end of a table tag (&#039;&#039;&#039;&amp;lt;/table&amp;gt;&#039;&#039;&#039;), so if you don&#039;t know where to place it (eg: if you&#039;re implementing CustomFields on a Dolibarr core module), just search for this tag in the page shown in the url (eg: /htdocs/commande/fiche.php).&lt;br /&gt;
&lt;br /&gt;
Eg: First load the hookmanager at the beginning of your php script:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Initialize technical object to manage hooks of thirdparties. Note that conf-&amp;gt;hooks_modules contains array array&lt;br /&gt;
include_once(DOL_DOCUMENT_ROOT.&#039;/core/class/hookmanager.class.php&#039;);&lt;br /&gt;
$hookmanager=new HookManager($db);&lt;br /&gt;
$hookmanager-&amp;gt;initHooks(array(&#039;yourmodule&#039;));&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then place the hook as many times as required:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// ... a lot of php code ...&lt;br /&gt;
&lt;br /&gt;
print &#039;&amp;lt;/td&amp;gt;&amp;lt;/tr&amp;gt;&#039;; // end of the last row of a table&lt;br /&gt;
&lt;br /&gt;
// Other attributes&lt;br /&gt;
$parameters=array(&#039;colspan&#039; =&amp;gt; &#039; colspan=&amp;quot;3&amp;quot;&#039;); // specifying the colspan is optional but will help CustomFields know how to correctly print the table&lt;br /&gt;
$reshook=$hookmanager-&amp;gt;executeHooks(&#039;formObjectOptions&#039;,$parameters,$object,$action);    // Note that $action and $object may have been modified by hook&lt;br /&gt;
&lt;br /&gt;
print &#039;&amp;lt;/table&amp;gt;&#039;; // end of the table, this is what you&#039;re looking for, place the hook just above&lt;br /&gt;
&lt;br /&gt;
// ... a lot of php code ... redo the same if you find another &amp;lt;/table&amp;gt; and there&#039;s a page where there&#039;s no custom fields but you want to place custom fields on there&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note2: If after implementing the hook you receive an error or custom fields don&#039;t appear, you may have to change $object to another variable, depending on what is called the main object (eg: in Agenda it&#039;s called $action and $actioncomm).&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Lines hooks&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
For product lines, you should anyway use the product lines template files: htdocs/core/tpl/freeproductline_edit.tpl.php&lt;br /&gt;
&lt;br /&gt;
If you don&#039;t, you can still manually implement the required hooks, what is required is that you supply $line (which is the current line object being processed) in the $parameters array:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$parameters=array(&#039;line&#039;=&amp;gt;$line,&#039;fk_parent_line&#039;=&amp;gt;$line-&amp;gt;fk_parent_line);&lt;br /&gt;
&lt;br /&gt;
echo $hookmanager-&amp;gt;executeHooks(&#039;formEditProductOptions&#039;,$parameters,$this,$action);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Hooks names:&lt;br /&gt;
* formEditProductOptions&lt;br /&gt;
* formCreateProductOptions ($line is not required here, because we create a new product line! you can leave $parameters=array(); empty).&lt;br /&gt;
Note: there&#039;s no hook for viewing, because showing the customfields will clutter the visual field, but if you really want you can also do it by adding a formViewProductOptions hook.&lt;br /&gt;
&lt;br /&gt;
=== Triggers ===&lt;br /&gt;
&lt;br /&gt;
First you need to know how to implement triggers in Dolibarr. For more informations about how to implement triggers in your module, please read the [[Triggers#Manage_and_create_a_new_trigger.27s_action|triggers wiki page]].&lt;br /&gt;
&lt;br /&gt;
An example case of implementation of hooks into a Dolibarr core file can be found in the [[Module_CustomFields_Cases|CustomFields Example Cases wiki page]].&lt;br /&gt;
&lt;br /&gt;
Implementing triggers on your module will allow CustomFields to save the fields at creation (datasheet edition is already managed by hooks).&lt;br /&gt;
&lt;br /&gt;
You need to implement 3 triggers:&lt;br /&gt;
- on creation of object - eg: YOURMODULE_CREATE&lt;br /&gt;
- on creation of lines - eg: YOURMODULELINE_INSERT (optional, only if your module has product lines)&lt;br /&gt;
- on update of lines - eg: YOURMODULELINE_UPDATE (optional, only if your module has product lines)&lt;br /&gt;
&lt;br /&gt;
Note: you need to supply the sql id of the newly created object before calling the trigger.&lt;br /&gt;
&lt;br /&gt;
Eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$this-&amp;gt;id = $this-&amp;gt;db-&amp;gt;last_insert_id(MAIN_DB_PREFIX.&amp;quot;yourmodule_table_element&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
// Appel des triggers&lt;br /&gt;
include_once(DOL_DOCUMENT_ROOT . &amp;quot;/core/class/interfaces.class.php&amp;quot;);&lt;br /&gt;
$interface=new Interfaces($this-&amp;gt;db);&lt;br /&gt;
$result=$interface-&amp;gt;run_triggers(&#039;YOURMODULE_CREATE&#039;,$this,$user,$langs,$conf);&lt;br /&gt;
if ($result &amp;lt; 0) {&lt;br /&gt;
    $error++; $this-&amp;gt;errors=$interface-&amp;gt;errors;&lt;br /&gt;
}&lt;br /&gt;
// Fin appel triggers&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note2: if it&#039;s a Dolibarr core module, you can easily find the right place to put the trigger by searching for &#039;create sql&#039; in the class subfolder (eg: URL is /htdocs/commande/fiche.php, the class is in /htdocs/commande/class/commande.class.php).&lt;br /&gt;
&lt;br /&gt;
Note3: CustomFields also supports a few other triggers like _CLONE, _UPDATE, _MODIFY, but they are not required and may not work depending on how your module works to manage these actions.&lt;br /&gt;
&lt;br /&gt;
== Configuring CustomFields to support the module ==&lt;br /&gt;
&lt;br /&gt;
=== Preliminary work ===&lt;br /&gt;
&lt;br /&gt;
You need to get the parameters of this module, and you will then put them in the CustomFields configuration file (conf_customfields.lib.php).&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Getting the parameters automatically&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
You can use the [[Module_CustomFields_Dev#Preliminary_test|preliminary test]] to easily get all those parameters.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Getting the parameters manually&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
To configure CustomFields to support a module, you need at least 5 parameters:&lt;br /&gt;
* the module&#039;s table_name (name of the sql table where the module stores its datas)&lt;br /&gt;
* the module&#039;s context (name given by the module to the hookmanager)&lt;br /&gt;
* the module&#039;s idvar (name of the variable that holds the sql row id of the record)&lt;br /&gt;
* the module&#039;s trigger action(s) (triggers on creation and update, see the related chapter above)&lt;br /&gt;
* the module&#039;s rights (for security purposes)&lt;br /&gt;
&lt;br /&gt;
To get these parameters manually, you can take a look inside the code of the module&#039;s files you want to implement:&lt;br /&gt;
* module&#039;s table name: either find it by yourself in the database (looks like llx_themodulename) or by printing it in actions_customfields.class.php by adding &#039;&#039;&#039;print_r($parameters)&#039;&#039;&#039; and search for $parameters-&amp;gt;table_element&lt;br /&gt;
* module&#039;s context: search for &amp;quot;callHooks(&amp;quot; without the quotes or take a look at the wiki: http://wiki.dolibarr.org/index.php/Hooks_system&lt;br /&gt;
or you can get it by printing it in the /htdocs/customfields/class/actions_customfields.class.php by adding print_r($parameters) and search for $parameters-&amp;gt;context&lt;br /&gt;
* module&#039;s trigger actions: search for &amp;quot;run_triggers(&amp;quot; or take a look at the wiki: http://wiki.dolibarr.org/index.php/Triggers#List_of_known_triggers_actions&lt;br /&gt;
* module&#039;s idvar: look at the url when you access one of the module&#039;s record, and you should see something like fiche.php?xxxid=12 - you need the xxxid (eg: socid, facid, etc.).&lt;br /&gt;
* module&#039;s security: in the page shown in URL when accessing the module&#039;s datasheet, edit this file and add &#039;&#039;&#039;print($user-&amp;gt;rights)&#039;&#039;&#039;, then in the list try to find the correct right (generally something like $user-&amp;gt;modulename-&amp;gt;creer). You can also try to search for $user-&amp;gt;rights in the sourcecode of this module.&lt;br /&gt;
&lt;br /&gt;
With these values, edit the config file (/htdocs/customfields/conf/conf_customfields.lib.php), particularly the $modulesarray and $triggersarray variables:&lt;br /&gt;
&lt;br /&gt;
=== Configure hooks and main parameters ===&lt;br /&gt;
&lt;br /&gt;
Add every parameters except triggers in the $modulesarray:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$modulesarray = array(&lt;br /&gt;
                      array(&#039;context&#039;=&amp;gt;&#039;invoicecard&#039;, &#039;table_element&#039;=&amp;gt;&#039;facture&#039;, &#039;idvar&#039;=&amp;gt;&#039;facid&#039;, &#039;rights&#039;=&amp;gt;array(&#039;$user-&amp;gt;rights-&amp;gt;facture-&amp;gt;creer&#039;)), // Client Invoice&lt;br /&gt;
                      array(&#039;context&#039;=&amp;gt;&#039;membercard&#039;, &#039;table_element&#039;=&amp;gt;&#039;adherent&#039;, &#039;idvar&#039;=&amp;gt;&#039;rowid&#039;, &#039;rights&#039;=&amp;gt;array(&#039;$user-&amp;gt;rights-&amp;gt;adherent-&amp;gt;creer&#039;), &#039;tabs&#039;=&amp;gt;array(&#039;objecttype&#039;=&amp;gt;&#039;member_admin&#039;, &#039;function&#039;=&amp;gt;&#039;member_admin_prepare_head&#039;, &#039;lib&#039;=&amp;gt;DOL_DOCUMENT_ROOT.&#039;/core/lib/member.lib.php&#039;)), // Members / Adherents&lt;br /&gt;
                     ); // Edit me to add the support of another module - NOTE: Lowercase only!&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are also a lot of others parameters you can give to CustomFields so that the module gets better managed or to add a few other functionnalities, here is a non-exhaustive list:&lt;br /&gt;
* context: name of the module&#039;s context.&lt;br /&gt;
* table_element: name of the sql table that holds the object&#039;s datas.&lt;br /&gt;
* idvar: name of the variable that holds the id of the object in the sql database (by default &#039;id&#039; or &#039;rowid&#039;).&lt;br /&gt;
* rights: prevent the custom fields to be editable unless all the rights set here are enabled (preventing custom fields to be viewed is useless because they won&#039;t show anyway if the page can&#039;t be loaded due to insufficient rights, so this is directly handled by the module).&lt;br /&gt;
* tabs=&amp;gt;array(objecttype, function, lib, tabname, tabtitle): used to embed CustomFields&#039;s admin panel into another module&#039;s admin page.&lt;br /&gt;
&lt;br /&gt;
=== Configure creation triggers ===&lt;br /&gt;
&lt;br /&gt;
Add the triggers in $triggersarray:&lt;br /&gt;
$triggersarray = array(&amp;quot;order_create&amp;quot;=&amp;gt;&amp;quot;commande&amp;quot;,&lt;br /&gt;
                       &amp;quot;yourmodule_triggeraction1&amp;quot;=&amp;gt;&amp;quot;yourmodule_tablename&amp;quot;,&lt;br /&gt;
                       &amp;quot;yourmodule_triggeraction2&amp;quot;=&amp;gt;&amp;quot;yourmodule_tablename&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
Note: generally you will be looking to implement the _CREATE.&lt;br /&gt;
&lt;br /&gt;
Note2: if a triggeraction is not recognized by CustomFields (eg: mymodule_addpayment) then by default CustomFields will assimilate it to a _create action (it will create and save the values for the custom fields, just for a module&#039;s creation page).&lt;br /&gt;
&lt;br /&gt;
Note3: CustomFields also supports a few other triggers like _CLONE, _UPDATE, _MODIFY, but they are not required and may not work depending on how your module works to manage these actions.&lt;br /&gt;
&lt;br /&gt;
=== Configuration troubleshooting ===&lt;br /&gt;
&lt;br /&gt;
* If you cannot see the custom fields appear on your module&#039;s page, this means the hooks are not implemented or configured in CustomFields, please read the related chapters.&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet but there&#039;s no edit button icon: the module implements security but you did not configure it properly in CustomFields, please find the right security in $user-&amp;gt;rights and set it in conf_customfields.lib.php.&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the creation page but are not saved: you forgot to set the trigger in $triggersarray, or the creation trigger does not exist for this module and you have to implement it. Jump to [[Module_CustomFields_Dev#Triggers]].&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet but not on the creation page (or vice versa): A hook is missing on the page where the custom fields do not appear. Jump to [[Module_CustomFields_Dev#Hooks]].&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet with the edit button, but when the edit button is clicked, everything disappear: either the $idvar is not properly set (look at the URL to know if you need to set idvar), or the hook is not properly made ($object is not defined and it&#039;s called by another name, but the hook use $object instead of the correct variable).&lt;br /&gt;
&lt;br /&gt;
* If the custom fields appear on the datasheet with the edit button, can be edited but cannot be saved (the data is not saved): same as the previous point: either $idvar or $object bug.&lt;br /&gt;
&lt;br /&gt;
Also you might find useful to &#039;&#039;&#039;[[Module_CustomFields#General_advices_for_troubleshooting|enable php errors]]&#039;&#039;&#039; to better track errors if that&#039;s not already done.&lt;br /&gt;
&lt;br /&gt;
= Example cases =&lt;br /&gt;
&lt;br /&gt;
You can find a compilation of concrete, practical examples on the [[Module_CustomFields_Cases]] wiki page.&lt;br /&gt;
&lt;br /&gt;
= TO DO =&lt;br /&gt;
&lt;br /&gt;
You will find below the public list of features that should be implemented in the future.&lt;br /&gt;
&lt;br /&gt;
If you have an idea about a new feature that isn&#039;t listed below, feel free to add it to the list!&lt;br /&gt;
&lt;br /&gt;
== To Do ==&lt;br /&gt;
Nothing here!&lt;br /&gt;
&lt;br /&gt;
== To Document ==&lt;br /&gt;
* Automatic recopy on cloning for custom fields and products&#039; lines custom fields.&lt;br /&gt;
* new API function: customfields_clone_or_recopy($object, $fromobject, $action2 = null)&lt;br /&gt;
&lt;br /&gt;
== Should do ==&lt;br /&gt;
* Add the possibility to mark a custom field to be shown in tables listings (eg: as a column in products listings) with &amp;quot;order by this column&amp;quot; buttons and stuffs. Will be difficult if the hooks are not yet implemented in listings.&lt;br /&gt;
* Checkbox (multiple checkboxes, one for each possible value) and multiselect (big select and can use SHIFT to multiselect) (store in XML file) (but impossible with constrained fields, can&#039;t store multiple foreign keys ids in one single sql field, but would work for other types like enum)&lt;br /&gt;
use fieldset, input checkbox and label for, eg:&lt;br /&gt;
    &amp;lt;fieldset&amp;gt;&amp;lt;legend&amp;gt;&amp;lt;label for=&amp;quot;cf_productslist&amp;quot;&amp;gt; $langs-&amp;gt;trans(&#039;cf_productslist&#039;)&amp;lt;/label&amp;gt;&amp;lt;/legend&amp;gt;&lt;br /&gt;
    &amp;lt;input type=&amp;quot;checkbox&amp;quot; name=&amp;quot;cf_productslist_choice1&amp;quot; value=&amp;quot;1&amp;quot; id=&amp;quot;cf_productslist_choice1&amp;quot; /&amp;gt;&amp;lt;label for=&amp;quot;cf_productslist_choice1&amp;quot;&amp;gt; $langs-&amp;gt;trans(&#039;cf_productslist_choice1&#039;)&amp;lt;/label&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
    &amp;lt;input type=&amp;quot;checkbox&amp;quot; name=&amp;quot;cf_productslist_choice2&amp;quot; value=&amp;quot;1&amp;quot; id=&amp;quot;cf_productslist_choice2&amp;quot; /&amp;gt;&amp;lt;label for=&amp;quot;cf_productslist_choice2&amp;quot;&amp;gt; $langs-&amp;gt;trans(&#039;cf_productslist_choice2&#039;)&amp;lt;/label&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
    etc...&lt;br /&gt;
    &amp;lt;/fieldset&amp;gt;&lt;br /&gt;
* Add a javascript options values generator for the enum type (a hidden input that would be shown only when DropdownBox type is selected and that would permit to add options by clicking a plus button).&lt;br /&gt;
* Replace triggers in customfields class by hookmanager?&lt;br /&gt;
* AJAX previsualisation of the HTML custom field that will be created.&lt;br /&gt;
* In ODTs, create special tags to directly access day, month or year value of a date type field independently of the rest.&lt;br /&gt;
* New overloading function&#039;s action: &amp;quot;get&amp;quot;, similar to view except that it works not only on Dolibarr&#039;s view but whenever someone tries to access this field&#039;s value.&lt;br /&gt;
* Implementation of custom fields in CSV exports (may need a few new hooks in Dolibarr?).&lt;br /&gt;
* Add Upload and Image field types (this would allow for an easier integration inside ODT and PDF templates than the current workflow of Dolibarr).&lt;br /&gt;
&lt;br /&gt;
== Known bugs ==&lt;br /&gt;
* in product and service modules, if you edit a field, the proposals and other fields below won&#039;t be shown, you need to refresh the page. This problem resides in Dolibarr I think (since we are simply using a hook).&lt;br /&gt;
* ficheinter (Intervention module) custom fields will stay in create mode just after create page is submitted (this is because of the $action that is not standard - $action=edit or something).&lt;br /&gt;
&lt;br /&gt;
== Never/Maybe one day ==&lt;br /&gt;
* Add support for repeatable (predefined) invoices (the way it is currently managed makes it very difficult to manage this without making a big exception, adding specific functions in customfields modules that would not at all will be reusable anywhere else, when customfields has been designed to be as generic as possible to support any module and any version of dolibarr, because it&#039;s managed by a totally different table while it&#039;s still managed by the same module, CustomFields work with the paradigm: one module, one table).&lt;br /&gt;
* Add an AJAX select box for constrained values : when a constrained type is selected and a table is selected, a hidden select box would show up with the list of the fields of this table to choose the values that will be printed as the values for this customfield (eg: for table llx_users you could select the &amp;quot;nom&amp;quot; field and then it would automatically prepend &amp;quot;nom_&amp;quot; to the field&#039;s name).&lt;br /&gt;
* Fine-grained rights management: rights per group, per user, and per field.&lt;br /&gt;
* Replace overloading functions (extend class) by hookmanager if possible (but how to simulate the *full switch? Plus this will force users to make their own modules to make those functions, and to disable/renable their modules everytime they will add a new function...).&lt;br /&gt;
* Refactor the trigger array: merge it with the modulesarray (along with a new way to specify which customfields trigger action a trigger should correspond, eg: &#039;linebill_insert&#039;=&amp;gt;&#039;customfields_create&#039;). But the triggers aren&#039;t necessarily associated with a specific module...&lt;/div&gt;</summary>
		<author><name>Lrq3000</name></author>
	</entry>
	<entry>
		<id>https://wiki.dolibarr.org/index.php?title=Hooks_system&amp;diff=33109</id>
		<title>Hooks system</title>
		<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php?title=Hooks_system&amp;diff=33109"/>
		<updated>2015-01-22T02:29:42Z</updated>

		<summary type="html">&lt;p&gt;Lrq3000: /* Add a hook to allow to insert code */ Precision&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{TemplateDocDevEn}}&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
Hooks are a developer feature available since Dolibarr 3.2.&lt;br /&gt;
It allows developers to add custom code to Dolibarr core without the need to resort to patching.&lt;br /&gt;
On the contrary of the [[triggers]] (another feature to interact with Dolibarr core code) − which are by definition linked to a business action − hooks can happen anywhere at anytime, they are entry points into the program.&lt;br /&gt;
* Hooks are enabled or not for a &amp;quot;context&amp;quot; (ie the module, eg: &amp;quot;productcard&amp;quot; for products, &amp;quot;invoicecard&amp;quot; for invoices, etc..). It&#039;s easy to find them, just search (or grep) for &amp;quot;&#039;&#039;&#039;initHooks(&#039;&#039;&#039;&amp;quot; into php files.&lt;br /&gt;
* Hooks are parts of code that can be completed or replaced by your own code. You can find all hookable part of code by searching (or grepping) for &amp;quot;&#039;&#039;&#039;executeHooks(&#039;&#039;&#039;&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
= Add a hook to allow to insert code =&lt;br /&gt;
To implement a hook in your own module (so that your module can be &#039;&#039;hooked&#039;&#039; by others), you have two steps to follow.&lt;br /&gt;
&lt;br /&gt;
These steps must be followed for every php script of your module where you want to implement hooks. And of course, this is also how hooks are implemented in every core Dolibarr&#039;s modules.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
1- Initialize the HookManager object (put this in the beginning of your script, just after includes):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Initialize technical object to manage hooks of thirdparties. Note that conf-&amp;gt;hooks_modules contains array array&lt;br /&gt;
include_once(DOL_DOCUMENT_ROOT.&#039;/core/class/hookmanager.class.php&#039;);&lt;br /&gt;
$hookmanager=new HookManager($db);&lt;br /&gt;
$hookmanager-&amp;gt;initHooks(array(&#039;context&#039;));&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
$hookmanager-&amp;gt;initHooks() takes 1 parameter (an array of contexts) and activate the hooks management for this script:&lt;br /&gt;
&lt;br /&gt;
- &#039;&#039;&#039;&#039;context&#039;&#039;&#039;&#039; is a context string. This is simply a indicator that hooking functions can use to detect in which context they are called.&lt;br /&gt;
&lt;br /&gt;
Note: you can set several contexts in the same init (for example if you want to have a common context into several pages and want also a specific context deidcated to your page).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2- Place hooks where you want to allow external code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$parameters=array();&lt;br /&gt;
$reshook=$hookmanager-&amp;gt;executeHooks(&#039;hookname&#039;,$parameters,$object,$action); // See description below&lt;br /&gt;
// Note that $action and $object may have been modified by hook&lt;br /&gt;
if (empty($reshook))&lt;br /&gt;
{&lt;br /&gt;
   ... // standard code that can be disabled/replaced by hook if return code &amp;gt; 0.&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;$hookmanager-&amp;gt;executeHooks()&#039;&#039;&#039; takes 4 parameters and add a hook (an entry point in your script for external functions):&lt;br /&gt;
&lt;br /&gt;
- &#039;&#039;&#039;&#039;hookname&#039;&#039;&#039;&#039; is the hook&#039;s name as a string (can be anything you want, or you can follow the Dolibarr&#039;s nomenclatura, look at the list of hooks below). eg: &#039;formObjectOptions&#039;&lt;br /&gt;
&lt;br /&gt;
- &#039;&#039;&#039;$parameters&#039;&#039;&#039; is a custom array to send more custom data to the hook (the hooking function can then process it). Place here anything you want, it can be a file, an array of strings, anything...&lt;br /&gt;
&lt;br /&gt;
eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$parameters=array(&#039;file&#039;=&amp;gt;&#039;my/path/to/a/file&#039;, &#039;customnames&#039;=&amp;gt;array(&#039;henry&#039;,&#039;david&#039;,&#039;john&#039;));&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
- &#039;&#039;&#039;$object&#039;&#039;&#039; is the object you want to pass onto the hooking function, mainly the current module&#039;s data (eg: invoice object if it is invoice module, etc..). This can be anything you want, but remember this will be the main component hooking functions will be using.&lt;br /&gt;
&lt;br /&gt;
- &#039;&#039;&#039;$action&#039;&#039;&#039; is a string indicating the current action (can be set to null or to something sensible like &#039;create&#039; or &#039;edit&#039;).&lt;br /&gt;
&lt;br /&gt;
Note: You will want to redo this step if you want to add multiple hooks at different places in your script.&lt;br /&gt;
&lt;br /&gt;
Now your module should be hookable, and you can follow the procedure below in &#039;&#039;&#039;Implement a hook&#039;&#039;&#039; to implement a hooking function that will take advantage of your new hooks.&lt;br /&gt;
&lt;br /&gt;
= Implement the Hook =&lt;br /&gt;
&lt;br /&gt;
To use a hook (add or replace a part of code), you first need to already have defined a module descriptor (see [[Module_development#Create_your_module_descriptor]]), and then you need to do 2 things:&lt;br /&gt;
&lt;br /&gt;
1- add your module to the list of contexts you want to hook on.&lt;br /&gt;
This means that when a context you hook will happen, your module hook code will be called.&lt;br /&gt;
To do that, just edit your &#039;&#039;&#039;/htdocs/&#039;&#039;yourmodulename&#039;&#039;/core/modules/mod&#039;&#039;YourModuleName&#039;&#039;.class.php&#039;&#039;&#039; and edit the &#039;&#039;&#039;$this-&amp;gt;module_parts&#039;&#039;&#039; variable with something like this:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$this-&amp;gt;module_parts = array(&lt;br /&gt;
&#039;hooks&#039; =&amp;gt; array(&#039;hookcontext1&#039;,&#039;hookcontext2&#039;)  // Set here all hooks context you want to support&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Of course change YourModuleName with your own module&#039;s name.&lt;br /&gt;
&lt;br /&gt;
Note: you can find the current module&#039;s context with print(&#039;Module context: &#039;.$object-&amp;gt;context); (put this inside the module&#039;s php file where the hook resides and that you want to hook).&lt;br /&gt;
&lt;br /&gt;
[[File:warning.png]] Be careful: do not forget to DISABLE then ENABLE the module in the admin panel to accept the new context, because these constants are only added/refreshed to the database when re-enabling your module.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2- You will overload a hook (a function) with your own.&lt;br /&gt;
&lt;br /&gt;
Create a file &#039;&#039;&#039;/htdocs/&#039;&#039;yourmodulename&#039;&#039;/class/actions_&#039;&#039;yourmodulename&#039;&#039;.class.php&#039;&#039;&#039; and then put something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class ActionsYourModuleName&lt;br /&gt;
{ &lt;br /&gt;
	/**&lt;br /&gt;
	 * Overloading the doActions function : replacing the parent&#039;s function with the one below&lt;br /&gt;
	 *&lt;br /&gt;
	 * @param   array()         $parameters     Hook metadatas (context, etc...)&lt;br /&gt;
	 * @param   CommonObject    &amp;amp;$object        The object to process (an invoice if you are in invoice module, a propale in propale&#039;s module, etc...)&lt;br /&gt;
	 * @param   string          &amp;amp;$action        Current action (if set). Generally create or edit or null&lt;br /&gt;
	 * @param   HookManager     $hookmanager    Hook manager propagated to allow calling another hook&lt;br /&gt;
	 * @return  int                             &amp;lt; 0 on error, 0 on success, 1 to replace standard code&lt;br /&gt;
	 */&lt;br /&gt;
	function doActions($parameters, &amp;amp;$object, &amp;amp;$action, $hookmanager)&lt;br /&gt;
	{&lt;br /&gt;
		$error = 0; // Error counter&lt;br /&gt;
		$myvalue = &#039;test&#039;; // A result value&lt;br /&gt;
&lt;br /&gt;
		print_r($parameters);&lt;br /&gt;
		echo &amp;quot;action: &amp;quot; . $action;&lt;br /&gt;
		print_r($object);&lt;br /&gt;
&lt;br /&gt;
		if (in_array(&#039;somecontext&#039;, explode(&#039;:&#039;, $parameters[&#039;context&#039;])))&lt;br /&gt;
		{&lt;br /&gt;
		  // do something only for the context &#039;somecontext&#039;&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		if (! $error)&lt;br /&gt;
		{&lt;br /&gt;
			$this-&amp;gt;results = array(&#039;myreturn&#039; =&amp;gt; $myvalue);&lt;br /&gt;
			$this-&amp;gt;resprints = &#039;A text to show&#039;;&lt;br /&gt;
			return 0; // or return 1 to replace standard code&lt;br /&gt;
		}&lt;br /&gt;
		else&lt;br /&gt;
		{&lt;br /&gt;
			$this-&amp;gt;errors[] = &#039;Error message&#039;;&lt;br /&gt;
			return -1;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Your function should now be called when you access the module/context and you will see the parameters and object and action.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Where&#039;&#039;&#039;:&lt;br /&gt;
* &#039;&#039;&#039;$parameters&#039;&#039;&#039; is an array of meta-data about the datas contained by the hook (eg: the context, always accessible by $parameters[&#039;context&#039;]).&lt;br /&gt;
* &#039;&#039;&#039;$object&#039;&#039;&#039; is the object that you may work on. Eg: the product if you are in the productcard context.&lt;br /&gt;
* &#039;&#039;&#039;$action&#039;&#039;&#039; is the action if one is conveyed (generally &amp;quot;create&amp;quot;, &amp;quot;edit&amp;quot; or &amp;quot;view&amp;quot;).&lt;br /&gt;
* &#039;&#039;&#039;$hookmanager&#039;&#039;&#039; is propagated only to allow your hook to call another hook.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Returns&#039;&#039;&#039;:&lt;br /&gt;
* The return code from a hook is 0 or 1 if success, negative if error. In general, it will be 0. It can be 1, which means that, in some specific cases, your code hook completely replaces what Dolibarr would do without the hook. If return code is negative, you can also set $this-&amp;gt;errors[]=&#039;Error message to report to user&#039;&lt;br /&gt;
* If the method sets the property $this-&amp;gt;results with an array, then the array $hookmanager-&amp;gt;resArray will automatically be enriched with the contents of this array, which can be reused later.&lt;br /&gt;
* If the method sets the property $this-&amp;gt; resprints with a string, then this string will be displayed by the manager hook (executeHook), immediately after your method exit.&lt;br /&gt;
* Your code can also modify the value of $object and $action.&lt;br /&gt;
&lt;br /&gt;
= List of available Hooks in Dolibarr =&lt;br /&gt;
How to find a complete list of all available hooks in Dolibarr&#039;s nomenclatura ? &lt;br /&gt;
Just search for &amp;quot;&#039;&#039;&#039;executeHooks(&#039;&#039;&#039;&amp;quot; through the whole sourcecode (generally called &amp;quot;find in files&amp;quot;) and you should easily find all the hooked methods calls.&lt;br /&gt;
&lt;br /&gt;
This is however a small list of them (not complete):&lt;br /&gt;
{{ListOfHooks}}&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
Note: please note that this list may change in the future as hooks and contexts are added into Dolibarr, so if you really need to find a specific hooks, please search directly in the sourcecode.&lt;br /&gt;
&lt;br /&gt;
= List of available Contexts in Dolibarr =&lt;br /&gt;
To find the complete list of available modules&#039; context in Dolibarr&#039;s nomenclatura, the procedure is similar to finding hooks.&lt;br /&gt;
Search for &amp;quot;&#039;&#039;&#039;initHooks(&#039;&#039;&#039;&amp;quot; through the whole sourcecode (generally called &amp;quot;find in files&amp;quot;) and you should find all the possible contexts.&lt;br /&gt;
&lt;br /&gt;
This is however a small list of them (not complete):&lt;br /&gt;
{{ListOfContexts}}&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
Note: please note that this list may change in the future as hooks and contexts are added into Dolibarr, so if you really need to find a specific hooks, please search directly in the sourcecode.&lt;br /&gt;
&lt;br /&gt;
= See also =&lt;br /&gt;
* [[Triggers]]&lt;br /&gt;
* [[Interfaces Dolibarr toward foreign systems]]&lt;br /&gt;
* [[Interfaces from foreign systems toward Dolibarr]]&lt;/div&gt;</summary>
		<author><name>Lrq3000</name></author>
	</entry>
	<entry>
		<id>https://wiki.dolibarr.org/index.php?title=Hooks_system&amp;diff=33108</id>
		<title>Hooks system</title>
		<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php?title=Hooks_system&amp;diff=33108"/>
		<updated>2015-01-22T02:28:30Z</updated>

		<summary type="html">&lt;p&gt;Lrq3000: /* Implement the Hook */ Precision&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{TemplateDocDevEn}}&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
Hooks are a developer feature available since Dolibarr 3.2.&lt;br /&gt;
It allows developers to add custom code to Dolibarr core without the need to resort to patching.&lt;br /&gt;
On the contrary of the [[triggers]] (another feature to interact with Dolibarr core code) − which are by definition linked to a business action − hooks can happen anywhere at anytime, they are entry points into the program.&lt;br /&gt;
* Hooks are enabled or not for a &amp;quot;context&amp;quot; (ie the module, eg: &amp;quot;productcard&amp;quot; for products, &amp;quot;invoicecard&amp;quot; for invoices, etc..). It&#039;s easy to find them, just search (or grep) for &amp;quot;&#039;&#039;&#039;initHooks(&#039;&#039;&#039;&amp;quot; into php files.&lt;br /&gt;
* Hooks are parts of code that can be completed or replaced by your own code. You can find all hookable part of code by searching (or grepping) for &amp;quot;&#039;&#039;&#039;executeHooks(&#039;&#039;&#039;&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
= Add a hook to allow to insert code =&lt;br /&gt;
To implement a hook in your own module (so that your module can be &#039;&#039;hooked&#039;&#039; by others), you have two steps to follow.&lt;br /&gt;
&lt;br /&gt;
These steps must be followed for every php script of your module where you want to implement hooks.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
1- Initialize the HookManager object (put this in the beginning of your script, just after includes):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Initialize technical object to manage hooks of thirdparties. Note that conf-&amp;gt;hooks_modules contains array array&lt;br /&gt;
include_once(DOL_DOCUMENT_ROOT.&#039;/core/class/hookmanager.class.php&#039;);&lt;br /&gt;
$hookmanager=new HookManager($db);&lt;br /&gt;
$hookmanager-&amp;gt;initHooks(array(&#039;context&#039;));&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
$hookmanager-&amp;gt;initHooks() takes 1 parameter (an array of contexts) and activate the hooks management for this script:&lt;br /&gt;
&lt;br /&gt;
- &#039;&#039;&#039;&#039;context&#039;&#039;&#039;&#039; is a context string. This is simply a indicator that hooking functions can use to detect in which context they are called.&lt;br /&gt;
&lt;br /&gt;
Note: you can set several contexts in the same init (for example if you want to have a common context into several pages and want also a specific context deidcated to your page).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2- Place hooks where you want to allow external code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$parameters=array();&lt;br /&gt;
$reshook=$hookmanager-&amp;gt;executeHooks(&#039;hookname&#039;,$parameters,$object,$action); // See description below&lt;br /&gt;
// Note that $action and $object may have been modified by hook&lt;br /&gt;
if (empty($reshook))&lt;br /&gt;
{&lt;br /&gt;
   ... // standard code that can be disabled/replaced by hook if return code &amp;gt; 0.&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;$hookmanager-&amp;gt;executeHooks()&#039;&#039;&#039; takes 4 parameters and add a hook (an entry point in your script for external functions):&lt;br /&gt;
&lt;br /&gt;
- &#039;&#039;&#039;&#039;hookname&#039;&#039;&#039;&#039; is the hook&#039;s name as a string (can be anything you want, or you can follow the Dolibarr&#039;s nomenclatura, look at the list of hooks below). eg: &#039;formObjectOptions&#039;&lt;br /&gt;
&lt;br /&gt;
- &#039;&#039;&#039;$parameters&#039;&#039;&#039; is a custom array to send more custom data to the hook (the hooking function can then process it). Place here anything you want, it can be a file, an array of strings, anything...&lt;br /&gt;
&lt;br /&gt;
eg:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$parameters=array(&#039;file&#039;=&amp;gt;&#039;my/path/to/a/file&#039;, &#039;customnames&#039;=&amp;gt;array(&#039;henry&#039;,&#039;david&#039;,&#039;john&#039;));&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
- &#039;&#039;&#039;$object&#039;&#039;&#039; is the object you want to pass onto the hooking function, mainly the current module&#039;s data (eg: invoice object if it is invoice module, etc..). This can be anything you want, but remember this will be the main component hooking functions will be using.&lt;br /&gt;
&lt;br /&gt;
- &#039;&#039;&#039;$action&#039;&#039;&#039; is a string indicating the current action (can be set to null or to something sensible like &#039;create&#039; or &#039;edit&#039;).&lt;br /&gt;
&lt;br /&gt;
Note: You will want to redo this step if you want to add multiple hooks at different places in your script.&lt;br /&gt;
&lt;br /&gt;
Now your module should be hookable, and you can follow the procedure below in &#039;&#039;&#039;Implement a hook&#039;&#039;&#039; to implement a hooking function that will take advantage of your new hooks.&lt;br /&gt;
&lt;br /&gt;
= Implement the Hook =&lt;br /&gt;
&lt;br /&gt;
To use a hook (add or replace a part of code), you first need to already have defined a module descriptor (see [[Module_development#Create_your_module_descriptor]]), and then you need to do 2 things:&lt;br /&gt;
&lt;br /&gt;
1- add your module to the list of contexts you want to hook on.&lt;br /&gt;
This means that when a context you hook will happen, your module hook code will be called.&lt;br /&gt;
To do that, just edit your &#039;&#039;&#039;/htdocs/&#039;&#039;yourmodulename&#039;&#039;/core/modules/mod&#039;&#039;YourModuleName&#039;&#039;.class.php&#039;&#039;&#039; and edit the &#039;&#039;&#039;$this-&amp;gt;module_parts&#039;&#039;&#039; variable with something like this:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$this-&amp;gt;module_parts = array(&lt;br /&gt;
&#039;hooks&#039; =&amp;gt; array(&#039;hookcontext1&#039;,&#039;hookcontext2&#039;)  // Set here all hooks context you want to support&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Of course change YourModuleName with your own module&#039;s name.&lt;br /&gt;
&lt;br /&gt;
Note: you can find the current module&#039;s context with print(&#039;Module context: &#039;.$object-&amp;gt;context); (put this inside the module&#039;s php file where the hook resides and that you want to hook).&lt;br /&gt;
&lt;br /&gt;
[[File:warning.png]] Be careful: do not forget to DISABLE then ENABLE the module in the admin panel to accept the new context, because these constants are only added/refreshed to the database when re-enabling your module.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2- You will overload a hook (a function) with your own.&lt;br /&gt;
&lt;br /&gt;
Create a file &#039;&#039;&#039;/htdocs/&#039;&#039;yourmodulename&#039;&#039;/class/actions_&#039;&#039;yourmodulename&#039;&#039;.class.php&#039;&#039;&#039; and then put something like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class ActionsYourModuleName&lt;br /&gt;
{ &lt;br /&gt;
	/**&lt;br /&gt;
	 * Overloading the doActions function : replacing the parent&#039;s function with the one below&lt;br /&gt;
	 *&lt;br /&gt;
	 * @param   array()         $parameters     Hook metadatas (context, etc...)&lt;br /&gt;
	 * @param   CommonObject    &amp;amp;$object        The object to process (an invoice if you are in invoice module, a propale in propale&#039;s module, etc...)&lt;br /&gt;
	 * @param   string          &amp;amp;$action        Current action (if set). Generally create or edit or null&lt;br /&gt;
	 * @param   HookManager     $hookmanager    Hook manager propagated to allow calling another hook&lt;br /&gt;
	 * @return  int                             &amp;lt; 0 on error, 0 on success, 1 to replace standard code&lt;br /&gt;
	 */&lt;br /&gt;
	function doActions($parameters, &amp;amp;$object, &amp;amp;$action, $hookmanager)&lt;br /&gt;
	{&lt;br /&gt;
		$error = 0; // Error counter&lt;br /&gt;
		$myvalue = &#039;test&#039;; // A result value&lt;br /&gt;
&lt;br /&gt;
		print_r($parameters);&lt;br /&gt;
		echo &amp;quot;action: &amp;quot; . $action;&lt;br /&gt;
		print_r($object);&lt;br /&gt;
&lt;br /&gt;
		if (in_array(&#039;somecontext&#039;, explode(&#039;:&#039;, $parameters[&#039;context&#039;])))&lt;br /&gt;
		{&lt;br /&gt;
		  // do something only for the context &#039;somecontext&#039;&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
		if (! $error)&lt;br /&gt;
		{&lt;br /&gt;
			$this-&amp;gt;results = array(&#039;myreturn&#039; =&amp;gt; $myvalue);&lt;br /&gt;
			$this-&amp;gt;resprints = &#039;A text to show&#039;;&lt;br /&gt;
			return 0; // or return 1 to replace standard code&lt;br /&gt;
		}&lt;br /&gt;
		else&lt;br /&gt;
		{&lt;br /&gt;
			$this-&amp;gt;errors[] = &#039;Error message&#039;;&lt;br /&gt;
			return -1;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Your function should now be called when you access the module/context and you will see the parameters and object and action.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Where&#039;&#039;&#039;:&lt;br /&gt;
* &#039;&#039;&#039;$parameters&#039;&#039;&#039; is an array of meta-data about the datas contained by the hook (eg: the context, always accessible by $parameters[&#039;context&#039;]).&lt;br /&gt;
* &#039;&#039;&#039;$object&#039;&#039;&#039; is the object that you may work on. Eg: the product if you are in the productcard context.&lt;br /&gt;
* &#039;&#039;&#039;$action&#039;&#039;&#039; is the action if one is conveyed (generally &amp;quot;create&amp;quot;, &amp;quot;edit&amp;quot; or &amp;quot;view&amp;quot;).&lt;br /&gt;
* &#039;&#039;&#039;$hookmanager&#039;&#039;&#039; is propagated only to allow your hook to call another hook.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Returns&#039;&#039;&#039;:&lt;br /&gt;
* The return code from a hook is 0 or 1 if success, negative if error. In general, it will be 0. It can be 1, which means that, in some specific cases, your code hook completely replaces what Dolibarr would do without the hook. If return code is negative, you can also set $this-&amp;gt;errors[]=&#039;Error message to report to user&#039;&lt;br /&gt;
* If the method sets the property $this-&amp;gt;results with an array, then the array $hookmanager-&amp;gt;resArray will automatically be enriched with the contents of this array, which can be reused later.&lt;br /&gt;
* If the method sets the property $this-&amp;gt; resprints with a string, then this string will be displayed by the manager hook (executeHook), immediately after your method exit.&lt;br /&gt;
* Your code can also modify the value of $object and $action.&lt;br /&gt;
&lt;br /&gt;
= List of available Hooks in Dolibarr =&lt;br /&gt;
How to find a complete list of all available hooks in Dolibarr&#039;s nomenclatura ? &lt;br /&gt;
Just search for &amp;quot;&#039;&#039;&#039;executeHooks(&#039;&#039;&#039;&amp;quot; through the whole sourcecode (generally called &amp;quot;find in files&amp;quot;) and you should easily find all the hooked methods calls.&lt;br /&gt;
&lt;br /&gt;
This is however a small list of them (not complete):&lt;br /&gt;
{{ListOfHooks}}&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
Note: please note that this list may change in the future as hooks and contexts are added into Dolibarr, so if you really need to find a specific hooks, please search directly in the sourcecode.&lt;br /&gt;
&lt;br /&gt;
= List of available Contexts in Dolibarr =&lt;br /&gt;
To find the complete list of available modules&#039; context in Dolibarr&#039;s nomenclatura, the procedure is similar to finding hooks.&lt;br /&gt;
Search for &amp;quot;&#039;&#039;&#039;initHooks(&#039;&#039;&#039;&amp;quot; through the whole sourcecode (generally called &amp;quot;find in files&amp;quot;) and you should find all the possible contexts.&lt;br /&gt;
&lt;br /&gt;
This is however a small list of them (not complete):&lt;br /&gt;
{{ListOfContexts}}&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
Note: please note that this list may change in the future as hooks and contexts are added into Dolibarr, so if you really need to find a specific hooks, please search directly in the sourcecode.&lt;br /&gt;
&lt;br /&gt;
= See also =&lt;br /&gt;
* [[Triggers]]&lt;br /&gt;
* [[Interfaces Dolibarr toward foreign systems]]&lt;br /&gt;
* [[Interfaces from foreign systems toward Dolibarr]]&lt;/div&gt;</summary>
		<author><name>Lrq3000</name></author>
	</entry>
	<entry>
		<id>https://wiki.dolibarr.org/index.php?title=Triggers_system&amp;diff=33107</id>
		<title>Triggers system</title>
		<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php?title=Triggers_system&amp;diff=33107"/>
		<updated>2015-01-22T02:23:21Z</updated>

		<summary type="html">&lt;p&gt;Lrq3000: /* Manage and create a new trigger&amp;#039;s action */ Fix include path for interfaces.class.php + enhance the php code&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{TemplateDocDevEn}}&lt;br /&gt;
&lt;br /&gt;
= Execute your own code on a Dolibarr&#039;s action =&lt;br /&gt;
To execute personalized code after a Dolibarr event (create/modify/remove a third party/invoice/product or other), Dolibarr offers a mechanism of business triggers. This mechanism allows you to personalize a work-flow to have Dolibarr business events being synchronized into another application for example.&lt;br /&gt;
You can also use triggers to modify the behavior of Dolibarr itself: for example, to create automatically a contract when an invoice is validated.&lt;br /&gt;
&lt;br /&gt;
So, to add your own code that will be triggered by a Dolibarr trigger, this is the way to process:&lt;br /&gt;
&lt;br /&gt;
1) Copy the trigger file &#039;&#039;&#039;htdocs/core/triggers/interface_90_all_Demo.class.php-NORUN&#039;&#039;&#039; under the new name:&lt;br /&gt;
* &#039;&#039;&#039;interface_99_all_&#039;&#039;Xxx&#039;&#039;.class.php&#039;&#039;&#039;&lt;br /&gt;
or&lt;br /&gt;
* &#039;&#039;&#039;interface_99_mod&#039;&#039;MyModule_Xxx&#039;&#039;.class.php&#039;&#039;&#039;&lt;br /&gt;
where &#039;&#039;Xxx&#039;&#039; is a string of your choice starting with uppercase character, &#039;&#039;99&#039;&#039; is the priority number for the execution of your trigger (1 being the top priority) and &#039;&#039;MyModule&#039;&#039; is the name of a module if you want that trigger is activated only if module MyModyle is activated. If you want the trigger to be always active, use &#039;&#039;all&#039;&#039; instead of  modMyModule.&lt;br /&gt;
This new file must be saved inside same directory.&lt;br /&gt;
&lt;br /&gt;
Note: Value you can use for modMyModule are values found in directory&lt;br /&gt;
&#039;&#039;htdocs/includes/modules&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WARNING&#039;&#039;&#039;: &#039;&#039;&#039;All&#039;&#039;&#039; those parameters are necessary for the trigger to be detected and executed! Eg: if you forget the priority number, your trigger won&#039;t be detected!&lt;br /&gt;
&lt;br /&gt;
For example, you can name your new trigger file: &lt;br /&gt;
&#039;&#039;htdocs/includes/triggers/interface_99_modFacture_Myworkflow.class.php&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
By creating such a file with name as in the example, your new trigger file will be executed each time a Dolibarr business event occurs but only if the module Facture is enabled.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Note&#039;&#039;&#039;: With Dolibarr 3.2+, you can also place the triggers in your own module&#039;s subdirectly. Eg: if your module resides in htdocs/mymodule/, then you can place your triggers inside htdocs/mymodule/core/triggers/. But for this, you must declare the trigger into your module descriptor file (eg: &#039;&#039;&#039;/mymodule/core/modules/modMyModule.class.php&#039;&#039;&#039;). For this, add into this file triggers-&amp;gt;1 into module_parts array:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Defined all module parts (triggers, login, substitutions, menus, etc...) (0=disable,1=enable)&lt;br /&gt;
$this-&amp;gt;module_parts = array(&#039;triggers&#039; =&amp;gt; 1);&lt;br /&gt;
//$this-&amp;gt;module_parts = array(&#039;triggers&#039; =&amp;gt; 1,&lt;br /&gt;
//              &#039;login&#039; =&amp;gt; 0,&lt;br /&gt;
//              &#039;substitutions&#039; =&amp;gt; 0,&lt;br /&gt;
//              &#039;menus&#039; =&amp;gt; 0);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Then disable and reenable your module. This will add a record into [[Table llx_const]] to say Dolibarr a trigger file must be searched into module directory &#039;&#039;&#039;htdocs/mymodule/core/triggers/&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2) Edit the file &#039;&#039;interface_99_modMyModule_Myworkflow.class.php&#039;&#039; to rename class &#039;&#039;InterfaceDemo&#039;&#039; by &#039;&#039;InterfaceMyworkflow&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Then go on page Home-&amp;gt; System Infos -&amp;gt; Dolibarr -&amp;gt; Triggers.&lt;br /&gt;
Your new trigger file must appears in the list without any error message. This means preceding actions have been done successfully.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
3) Return now to edition of file to add your own code inside function  &#039;&#039;run_trigger&#039;&#039;.&lt;br /&gt;
This function is called at each Dolibarr business event. Put your code according to events on which you want to run. Each event is identified by a code (see following chapter to get full list of codes). So you can react or no after an event by doing just a test on variable &#039;&#039;&#039;$action&#039;&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
  function run_trigger($action,$object,$user,$langs,$conf)&lt;br /&gt;
  {&lt;br /&gt;
        // Put here code to execute after an action&lt;br /&gt;
        // The type of Dolibarr business event is stored into $action&lt;br /&gt;
        // Data of action are stored into object $object&lt;br /&gt;
        // Setup, user and language are saved into $conf, $user and $lang&lt;br /&gt;
        if ($action == &#039;COMPANY_CREATE&#039;)&lt;br /&gt;
        {&lt;br /&gt;
            dol_syslog(&amp;quot;Trigger for action &#039;$action&#039; launched. id=&amp;quot;.$object-&amp;gt;id);&lt;br /&gt;
        }&lt;br /&gt;
        elseif ($action == &#039;COMPANY_MODIFY&#039;)&lt;br /&gt;
        {        &lt;br /&gt;
            dol_syslog(&amp;quot;Trigger for action &#039;$action&#039; launched. id=&amp;quot;.$object-&amp;gt;id);&lt;br /&gt;
        }&lt;br /&gt;
        elseif ($action == &#039;COMPANY_DELETE&#039;)&lt;br /&gt;
        ...&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can do what you want into this coding part, if you check that function run_trigger returns a return code according to following rule:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;0 if KO, 0 if nothing is done, &amp;gt;0 if OK&lt;br /&gt;
&lt;br /&gt;
You can also use, inside this function, the following preinitialized variables:&lt;br /&gt;
* &#039;&#039;&#039;$object&#039;&#039;&#039; is the object on which action is done (see next chapter)&lt;br /&gt;
* &#039;&#039;&#039;$user&#039;&#039;&#039; is the object containing all information of Dolibarr user that realizes the business event.&lt;br /&gt;
* &#039;&#039;&#039;$langs&#039;&#039;&#039; is the object that contains the user language and translations strings&lt;br /&gt;
* &#039;&#039;&#039;$conf&#039;&#039;&#039; is the object that contains all the Dolibarr configuration.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4) Once your trigger file has been developed, last action is to test by using Dolibarr to execute the business event that should trigger your code. Warning, call of function &#039;&#039;&#039;run_trigger&#039;&#039;&#039; is encapsulated into a transaction. If your code return a KO code, the calling function may rollback the transaction (this depends on calling function).&lt;br /&gt;
Add log information inside your function &#039;&#039;&#039;run_trigger&#039;&#039;&#039; to know if your code is correctly triggered and runs correctly. For this, you can use the function&lt;br /&gt;
dol_syslog(&amp;quot;my log message&amp;quot;, LOG_DEBUG);&lt;br /&gt;
&lt;br /&gt;
= List of known triggers actions =&lt;br /&gt;
List of all events that launch triggers are available in [[Table llx c action trigger]]. This is a quick list of those events, identified by value of variable &#039;&#039;&#039;$action&#039;&#039;&#039; into trigger code and type of variable &#039;&#039;&#039;$object&#039;&#039;&#039; for each code.&lt;br /&gt;
&lt;br /&gt;
In most cases, trigger action code is a key matching this rules:&lt;br /&gt;
* Start with module name&lt;br /&gt;
* Other information (isolated with character _)&lt;br /&gt;
* Action&lt;br /&gt;
&lt;br /&gt;
{{TemplateListOfTriggers}}&lt;br /&gt;
This list may be not complete.&lt;br /&gt;
&lt;br /&gt;
= Manage and create a new trigger&#039;s action =&lt;br /&gt;
&lt;br /&gt;
To manage other events on objects than the one defined before, you must edit the Dolibarr code to add the following code inside the business method of the class file of the object:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Call triggers&lt;br /&gt;
include_once(DOL_DOCUMENT_ROOT . &amp;quot;/core/class/interfaces.class.php&amp;quot;);&lt;br /&gt;
$interface=new Interfaces($this-&amp;gt;db);&lt;br /&gt;
$result=$interface-&amp;gt;run_triggers(&#039;XXXXX_YYYYY&#039;,$this,$user,$langs,$conf);&lt;br /&gt;
if ($result &amp;lt; 0) { $error++; $this-&amp;gt;errors=$interface-&amp;gt;errors; }&lt;br /&gt;
// End call triggers&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;With Dolibarr &amp;gt;= 3.7 you must use this piece of code for new trigger :&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Call trigger&lt;br /&gt;
include_once(DOL_DOCUMENT_ROOT . &amp;quot;/core/class/interfaces.class.php&amp;quot;);&lt;br /&gt;
$interface=new Interfaces($this-&amp;gt;db);&lt;br /&gt;
$result=$this-&amp;gt;call_trigger(&#039;XXXXX_YYYYY&#039;,$user); // this is the new way to call triggers with Dolibarr &amp;gt;= 3.7.0&lt;br /&gt;
if ($result &amp;lt; 0) { $error++; $this-&amp;gt;errors=$interface-&amp;gt;errors; }&lt;br /&gt;
// End call triggers&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Here, &#039;&#039;&#039;$this&#039;&#039;&#039; contains the business class with all information to send to the trigger functions. Replace &#039;XXXXX_YYYYY&#039; by an event code not already used.&lt;br /&gt;
Then you will be able to add inside the run_trigger method, a &amp;quot;if&amp;quot; to manage this code. The run_trigger method will contains a part of code like this :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function run_trigger($action,$object,$user,$lang,$conf)&lt;br /&gt;
{&lt;br /&gt;
      // Put here code to execute after an action&lt;br /&gt;
      // The type of Dolibarr business event is stored into $action&lt;br /&gt;
      // Data of action are stored into object $object&lt;br /&gt;
      // Setup, user and language are saved into $conf, $user and $lang&lt;br /&gt;
      if ($action == &#039;XXXXX_YYYYY&#039;)&lt;br /&gt;
      {&lt;br /&gt;
          dol_syslog(&amp;quot;Trigger for action &#039;$action&#039; launched. id=&amp;quot;.$object-&amp;gt;id);&lt;br /&gt;
      }&lt;br /&gt;
      elseif ($action == &#039;COMPANY_CREATE&#039;)&lt;br /&gt;
      {&lt;br /&gt;
          dol_syslog(&amp;quot;Trigger for action &#039;$action&#039; launched. id=&amp;quot;.$object-&amp;gt;id);&lt;br /&gt;
      }&lt;br /&gt;
      elseif ($action == &#039;COMPANY_MODIFY&#039;)&lt;br /&gt;
      {        &lt;br /&gt;
          dol_syslog(&amp;quot;Trigger for action &#039;$action&#039; launched. id=&amp;quot;.$object-&amp;gt;id);&lt;br /&gt;
      }&lt;br /&gt;
      elseif ($action == &#039;COMPANY_DELETE&#039;)&lt;br /&gt;
      ...&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Conclusion =&lt;br /&gt;
You can in few minutes, add an interface from Dolibarr towards another system (or why not towards Dolibarr itself), with no risk since you don&#039;t touch anything to Dolibarr files. You only &amp;quot;added&amp;quot; a new trigger file inside the trigger directory.&lt;br /&gt;
If this new interface can serve a lot of other users, you can package it into a tgz file (see page [[Module_development#Create_a_package_to_distribute_and_install_your_module]]) and submit it on the download area in section external contributions on official Dolibarr web site.&lt;br /&gt;
&lt;br /&gt;
= See also =&lt;br /&gt;
* [[Hooks system]]&lt;br /&gt;
* [[Interfaces Dolibarr toward foreign systems]]&lt;br /&gt;
* [[Interfaces from foreign systems toward Dolibarr]]&lt;/div&gt;</summary>
		<author><name>Lrq3000</name></author>
	</entry>
	<entry>
		<id>https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33081</id>
		<title>Module CustomFields Cases</title>
		<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33081"/>
		<updated>2015-01-16T14:14:20Z</updated>

		<summary type="html">&lt;p&gt;Lrq3000: /* Refresh the price instantly */ add an if statement&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:CustomFields]]&lt;br /&gt;
{{TemplateDocDevEn}}&lt;br /&gt;
{{TemplateModEN}}&lt;br /&gt;
{{BasculeDevUserEn|&lt;br /&gt;
name=CustomFields|&lt;br /&gt;
num=8500|&lt;br /&gt;
devdoc=[[Module_CustomFields_Dev]]|&lt;br /&gt;
userdoc=[[Module_CustomFields]]|}}&lt;br /&gt;
This page lists a compilation of concrete, practical examples uses of [[Module_CustomFields|CustomFields]] and of its various features.&lt;br /&gt;
&lt;br /&gt;
If you had an experience with CustomFields, you can also add here your case as an example for others to get inspired.&lt;br /&gt;
&lt;br /&gt;
= How to make a generic PDF template listing all custom fields =&lt;br /&gt;
&lt;br /&gt;
To make a generic PDF template listing all custom fields, you just need to take an existing PDF template, then you need to follow these 3 steps:&lt;br /&gt;
&lt;br /&gt;
1/ Edit the class name (required, else it will conflict with the original template).&lt;br /&gt;
&lt;br /&gt;
Eg: if we take the crabe template for invoices, we change:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;crabe&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
into:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_customfields extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;customfields&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2/ Copy this generic function at the end of the PDF:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propale/order/etc... (CustomFields simpler functions will automatically adapt)&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs); // set default PDF font size&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
    // Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
    $customfields = customfields_fill_object($object, null, $outputlangs, null, true);&lt;br /&gt;
&lt;br /&gt;
    // Setting the starting position of the text cursor&lt;br /&gt;
    $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
    $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
    // Printing the customfields&lt;br /&gt;
    foreach ($object-&amp;gt;customfields as $label=&amp;gt;$value) { // $value is already formatted!&lt;br /&gt;
        // Get translated label&lt;br /&gt;
        $translatedlabel = $customfields-&amp;gt;findLabelPDF($label, $outputlangs); // translated label of the customfield (not translated by default in customfields_fill_object() because a field should always be accessible by a base name, whatever the translation is)&lt;br /&gt;
&lt;br /&gt;
        // PDF formatting, placement and printing&lt;br /&gt;
        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
        $pdf-&amp;gt;MultiCell(0,3, $translatedlabel.&#039; ($object-&amp;gt;customfields-&amp;gt;&#039;.$label.&#039;): &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternative function (with a bit less functionalities, but more controllable):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page (used to debug if CustomFields setup is correct)&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propal/product/whatever...&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs);&lt;br /&gt;
&lt;br /&gt;
            if (empty($object-&amp;gt;table_element) or empty($object-&amp;gt;id)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;Current \$object is not compatible with CustomFields, could not find table_element or id.&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
    $customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element);&lt;br /&gt;
&lt;br /&gt;
            // Fetching custom fields records&lt;br /&gt;
            $fields = $customfields-&amp;gt;fetch($object-&amp;gt;id);&lt;br /&gt;
&lt;br /&gt;
            if (!isset($fields)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;No custom field could be found for this object. Please check your configuration (did you create at least one customfield and set a value in the current datasheet?)&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            } else {&lt;br /&gt;
                // Setting the starting position of the text cursor&lt;br /&gt;
                $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
                $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
                // Printing the customfields&lt;br /&gt;
                foreach ($fields as $key=&amp;gt;$field) {&lt;br /&gt;
                        $translatedname = $customfields-&amp;gt;findLabelPDF($key, $outputlangs); // label of the customfield&lt;br /&gt;
                        $value = $customfields-&amp;gt;simpleprintFieldPDF($key, $field, $outputlangs); // value (cleaned and properly formatted) of the customfield&lt;br /&gt;
&lt;br /&gt;
                        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
                        $pdf-&amp;gt;MultiCell(0,3, $translatedname.&#039;: &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
                        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3/ Call the _pagecustomfields function at the right place (generally, after _pagefoot and just before $pdf-&amp;gt;Close()):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Pied de page&lt;br /&gt;
$this-&amp;gt;_pagefoot($pdf,$object,$outputlangs);&lt;br /&gt;
$pdf-&amp;gt;AliasNbPages();&lt;br /&gt;
&lt;br /&gt;
// CustomFields&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) { // if the customfields module is activated...&lt;br /&gt;
        // Add a page with the customfields&lt;br /&gt;
        $pdf-&amp;gt;AddPage();&lt;br /&gt;
        $this-&amp;gt;_pagecustomfields($pdf,$object,$outputlangs);&lt;br /&gt;
        $pagenb++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
$pdf-&amp;gt;Close(); // you should first search for this, you should only find one occurrence&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then go to the module&#039;s admin panel (eg: invoice admin panel) and enable the template you&#039;ve just created.&lt;br /&gt;
&lt;br /&gt;
You can now use it to test if your customfields work.&lt;br /&gt;
&lt;br /&gt;
= Conditions on fields selection =&lt;br /&gt;
&lt;br /&gt;
Sometimes you may want to show a DropdownBox or Constraint (or another multi-choice type), but you may want to show or hide choices based on conditions (eg: only if it&#039;s a prospect, only if this zone was selected, only if this category was selected, etc..).&lt;br /&gt;
&lt;br /&gt;
To know which is the best solution you should choose to implement your condition, you should first know in which category your condition falls in.&lt;br /&gt;
&lt;br /&gt;
== Conditions categories ==&lt;br /&gt;
&lt;br /&gt;
Conditions can be categorized into 4 types:&lt;br /&gt;
&lt;br /&gt;
* static condition: choices are restrained on a condition that never changes (eg: only show third-parties that are suppliers, or lowercase all data on sql insertion, or check that a field is above a certain number, etc.).&lt;br /&gt;
* semi-dynamic condition: choices that are constrained on a static table but based on the value of another field/customfield. This is also commonly called &amp;quot;Cascaded dropdown lists&amp;quot; or &amp;quot;Dynamically linked dropdown lists&amp;quot;.&lt;br /&gt;
* dynamic: everything is dynamic: choices are based on a subfield of a field of the current module, so that there&#039;s no material table and it&#039;s impossible to create a view (because you would need to create one view per id) (eg: show only the contacts attached to the third-party being accessed)&lt;br /&gt;
* timed condition: a condition that is based on time or recurrence (eg: at 5:00 AM delete old records, etc..)&lt;br /&gt;
&lt;br /&gt;
== Conditions solutions ==&lt;br /&gt;
&lt;br /&gt;
Now that you know what kind of condition you want to set, you can choose one of the following solutions depending on your preferences and knowledge.&lt;br /&gt;
&lt;br /&gt;
In any case, you can always use the &#039;&#039;&#039;CustomFields&#039;s overload functions&#039;&#039;&#039; to fit your needs for nearly every type of condition.&lt;br /&gt;
&lt;br /&gt;
=== Static condition ===&lt;br /&gt;
&lt;br /&gt;
* View: create a view on the table based on the condition, and then create a custom field constrained on this view (which is just like any table), eg: only show third-parties that are suppliers:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
CREATE VIEW llx_societe_supplier_view AS&lt;br /&gt;
SELECT *&lt;br /&gt;
FROM llx_societe&lt;br /&gt;
WHERE fournisseur=1;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
WARNING: foreign keys (and thus constraints) can&#039;t be used on views with MySQL, and materialized views neither exist, so you can&#039;t use this method with MySQL.&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
* SQL Transformations&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
=== Semi-dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade option is ideal in most cases, and if not enough, you can use the Cascade Custom option and Overloading Functions.&lt;br /&gt;
* View&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
&lt;br /&gt;
=== Dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade Custom and Overload functions&lt;br /&gt;
* Making your own module, which calls the CustomFields class&lt;br /&gt;
&lt;br /&gt;
=== Timed condition ===&lt;br /&gt;
&lt;br /&gt;
* SQL scheduled events&lt;br /&gt;
* Cron job&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
= Overloading functions =&lt;br /&gt;
&lt;br /&gt;
Here are a few concrete use cases of overloading functions to achieve various goals.&lt;br /&gt;
&lt;br /&gt;
== Dynamic list selection based on another one ==&lt;br /&gt;
&lt;br /&gt;
The goal is to show two custom fields on the Third-Party module: one which gives the zone (secteur) of the third-party (Isere, Alpes du sud, Haute-Savoie...) and the other which gives relative to the zone the list of all the ski resorts (station_a) inside the selected zone.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will show how to manually manage this kind of requirements using [[Module_CustomFields#Overloading_functions:_Adding_your_own_management_code_for_custom_fields|overloading functions]], but in latest CustomFields releases, the same solution can be achieved automatically using the [[Module_CustomFields#Cascade|Cascade option]].&lt;br /&gt;
&lt;br /&gt;
We have one table &#039;&#039;&#039;llx_k_station&#039;&#039;&#039; (rowid, station (char50), secteur(char50)) which contains the following: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1,les 2 alpes,isère&lt;br /&gt;
2,chamrousse,isère&lt;br /&gt;
3,alpe d&#039;huez,isère&lt;br /&gt;
4,vars,alpes du sud&lt;br /&gt;
5,risoul,alpes du sud&lt;br /&gt;
etc...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In CustomFields&#039;s admin panel, we create two custom fields:&lt;br /&gt;
&lt;br /&gt;
1- secteur: returns the list of all zones - type DropdownBox: enum(&#039;- Aucun&#039;,&#039;Alpes du Sud&#039;,&#039;export&#039;,&#039;Haute-Savoie&#039;,&#039;Isère&#039;, etc...)&lt;br /&gt;
&lt;br /&gt;
2- station_a: returns the list of all ski resorts - type constraint on &#039;&#039;&#039;llx_k_station&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Here is the code to put in customfields_fields_extend.lib.php that will allow to show only the ski resorts corresponding to the selected zone:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_editview_societe_station_a (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$parameters, &amp;amp;$action, &amp;amp;$user, &amp;amp;$idvar, &amp;amp;$rightok, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
  global $db; // allow to access database functions&lt;br /&gt;
  $sql=&amp;quot;SELECT llx_k_station.station, llx_k_station.rowid FROM llx_societe_customfields INNER JOIN llx_k_station ON llx_societe_customfields.Secteur = llx_k_station.secteur WHERE llx_societe_customfields.fk_societe=&amp;quot;.$object-&amp;gt;id;&lt;br /&gt;
$result=$db-&amp;gt;query($sql);&lt;br /&gt;
 &lt;br /&gt;
  if ($result) {&lt;br /&gt;
    $num = $db-&amp;gt;num_rows($result);&lt;br /&gt;
    $i = 0;&lt;br /&gt;
    $myarray = array();&lt;br /&gt;
    while ($i &amp;lt; $num)&lt;br /&gt;
    {&lt;br /&gt;
      $obj = $db-&amp;gt;fetch_object($result);&lt;br /&gt;
      $myarray []=array(&#039;id&#039;=&amp;gt;$obj-&amp;gt;rowid, &#039;value&#039;=&amp;gt;$obj-&amp;gt;station);&lt;br /&gt;
      $i++;&lt;br /&gt;
    }&lt;br /&gt;
    $value = $myarray; &lt;br /&gt;
    $db-&amp;gt;free($result);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to manub for giving this case.&lt;br /&gt;
&lt;br /&gt;
== Dynamic discount computation per products lines on propales ==&lt;br /&gt;
&lt;br /&gt;
The goal here is to compute the discount for a product line in propales based on a computation on three custom fields.&lt;br /&gt;
&lt;br /&gt;
First, create three custom fields for &amp;quot;Line commercial proposals&amp;quot;: vl, cpl and lc.&lt;br /&gt;
&lt;br /&gt;
Then add the following overloading function at the end of your customfields_fields_extend.lib.php file (read the comments for more informations):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_propaldet_vl (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
&lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $vl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;vl&#039;};&lt;br /&gt;
    $cpl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;cpl&#039;};&lt;br /&gt;
    $lc = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;lc&#039;};&lt;br /&gt;
&lt;br /&gt;
    // Compute the discount&lt;br /&gt;
    $remise_calculee = (100 - $vl) * (100 - $cpl) * (100 - $lc);&lt;br /&gt;
&lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $remise_calculee = price2num($remise_calculee);&lt;br /&gt;
&lt;br /&gt;
    // Assign the new discount into the PropalLine object&lt;br /&gt;
    $object-&amp;gt;remise_percent = $remise_calculee;&lt;br /&gt;
&lt;br /&gt;
    // Update the price (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
&lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
&lt;br /&gt;
    //$q = &amp;quot;UPDATE  &amp;quot;.MAIN_DB_PREFIX.&amp;quot;propaldet SET remise_percent=&amp;quot;.$remise_calculee.&amp;quot; WHERE  rowid=&amp;quot;.$object-&amp;gt;rowid;&lt;br /&gt;
    //$db-&amp;gt;query($q);&lt;br /&gt;
    &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;vl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;cpl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;lc&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Linking Dolibarr objects from two different modules =&lt;br /&gt;
&lt;br /&gt;
Dolibarr sometimes offers the possibility to link two different modules. Eg: when you convert an Intervention card into an Invoice.&lt;br /&gt;
&lt;br /&gt;
In this case, you might want in your templates to fetch the custom fields of the linked object. Eg: you generate a PDF from the Invoice, but you want to get the custom fields from the Intervention that was converted into the Invoice.&lt;br /&gt;
&lt;br /&gt;
The key here is to use the &#039;&#039;&#039;llx_element_element&#039;&#039;&#039; table or the &#039;&#039;&#039;llx_element_*&#039;&#039;&#039; tables (eg: llx_element_contact): this is a standard Dolibarr table that stores all links between objects of two different modules.&lt;br /&gt;
&lt;br /&gt;
Here is the structure:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
rowid | fk_source | sourcetype | fk_target | targettype&lt;br /&gt;
-------------------------------------------------------&lt;br /&gt;
1 | 2 | fichinter | 5 | facture&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can then use a php code like the following to populate your $object with customfields:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
// This will also create and returns us an instance of the CustomFields class, which we will use to manually query the llx_element_element table&lt;br /&gt;
$customfields = customfields_fill_object($object, null, $outputlangs, &#039;facture&#039;, true); // beautified values&lt;br /&gt;
&lt;br /&gt;
// Querying the llx_element_element table manually to get the link between the invoice and the linked intervention card (we want the id of the intervention card)&lt;br /&gt;
// fetchAny($columns, $table, $where=&#039;&#039;, $orderby=&#039;&#039;, $limitby=&#039;&#039;)&lt;br /&gt;
$eltrow = $customfields-&amp;gt;fetchAny(&#039;*&#039;, MAIN_DB_PREFIX.&#039;element_element&#039;, &#039;targettype=&amp;quot;&#039;.$object-&amp;gt;element.&#039;&amp;quot; and fk_target=&#039;.$object-&amp;gt;id.&#039; and sourcetype=&amp;quot;fichinter&amp;quot;&#039;);&lt;br /&gt;
&lt;br /&gt;
// Save the intervention&#039;s id&lt;br /&gt;
$sourceid = $eltrow[0][&#039;fk_source&#039;];&lt;br /&gt;
&lt;br /&gt;
// Crafting a dummy intervention object&lt;br /&gt;
$fromobject = new stdClass(); // Init an empty object&lt;br /&gt;
$fromobject-&amp;gt;id = $sourceid; // We need the id and..&lt;br /&gt;
$fromobject-&amp;gt;table_element = &#039;fichinter&#039;; // the table_element (name of the table)&lt;br /&gt;
&lt;br /&gt;
// We can then fill $object with the intervention custom fields (we store them in a sub property &#039;fichinter&#039; to avoid conflicts with invoice&#039;s custom fields)&lt;br /&gt;
customfields_fill_object($object, $fromobject, $outputlangs, &#039;fichinter&#039;, true);&lt;br /&gt;
&lt;br /&gt;
// Now you can access invoices custom fields using:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;facture-&amp;gt;cf_myfield);&lt;br /&gt;
// And access intervention custom fields with:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;fichinter-&amp;gt;cf_myfield);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to netassopro for the tip.&lt;br /&gt;
&lt;br /&gt;
= Modify the total global price with a coefficient (without tampering the discount) =&lt;br /&gt;
&lt;br /&gt;
This is more troublesome than modifying the total price per product/service line, because the total global price of an object (let&#039;s say an invoice, but this also works for any other object) is always recomputed by summing the prices and quantity of each product/service line. This means that although there&#039;s an entry &amp;quot;total_ttc&amp;quot; in the database to store the invoice&#039;s total price, this price is in fact always recomputed, it&#039;s never used as-is. Thus, it&#039;s useless to change only the database record: we need to inject some code to change how the total price is computed.&lt;br /&gt;
&lt;br /&gt;
This could be done using an overloading function or a trigger, but unluckily, Dolibarr doesn&#039;t call a trigger nor a hook when recomputing the price. We thus need to edit core files to do what we want.&lt;br /&gt;
&lt;br /&gt;
However, the bright side is that we can cleverly edit just two functions from one core file: update_price() and getMarginInfos() from /htdocs/core/class/commonobject.class.php , and everything should run smoothly. Beware that if you update Dolibarr, you will need to redo these two modifications in the code.&lt;br /&gt;
&lt;br /&gt;
First, you need to create a new custom field called &amp;quot;global_coefficient&amp;quot; in Invoices (not Invoices Lines!). Then you can continue to the subchapters below.&lt;br /&gt;
&lt;br /&gt;
Also note that this global coefficient is also compatible with per line coefficients (as described in the next chapter), thus you can use a global coefficient and per line coefficients at the same time. You can also use multiple global coefficient if you wish.&lt;br /&gt;
&lt;br /&gt;
Another note: if you have a grid of discounts depending on quantity or price or whatever parameter, you can program a precomputation code using a Custom Ajax function (this way, your discounts will be automatically computed, but then you can still manually enter them for particular cases since the custom field is editable).&lt;br /&gt;
&lt;br /&gt;
== Implement the change of global price in most of Dolibarr (including remaining debt to pay and PDF and ODT) ==&lt;br /&gt;
&lt;br /&gt;
The change we will implement here will reflect the new total price with the global coefficient in most of Dolibarr&#039;s code, since we will here change the main function that is usually called to update and get the total price.&lt;br /&gt;
&lt;br /&gt;
Open /htdocs/core/class/commonobject.class.php and look for the function update_price().&lt;br /&gt;
&lt;br /&gt;
You should see the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function update_price($exclspec=0,$roundingadjust=&#039;none&#039;,$nodatabaseupdate=0,$seller=&#039;&#039;)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just below the include_once line, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;;&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/conf/conf_customfields_func.lib.php&#039;;&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $this-&amp;gt;table_element);&lt;br /&gt;
// Get object&#039;s id (either stored in property &amp;quot;id&amp;quot; or &amp;quot;rowid&amp;quot;)&lt;br /&gt;
$this_id = (!empty($this-&amp;gt;id)) ? $this-&amp;gt;id : $this-&amp;gt;rowid;&lt;br /&gt;
// Load custom fields records&lt;br /&gt;
$cf_parent_record = $customfields-&amp;gt;fetch($this_id);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will load the global_coefficient custom field (and also any other custom field you have defined for invoices).&lt;br /&gt;
&lt;br /&gt;
Now scroll below, and you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
while ($i &amp;lt; $num)&lt;br /&gt;
{&lt;br /&gt;
    $obj = $this-&amp;gt;db-&amp;gt;fetch_object($resql);&lt;br /&gt;
&lt;br /&gt;
    ... (lots of code)&lt;br /&gt;
&lt;br /&gt;
    i++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Add revenue stamp to total&lt;br /&gt;
$this-&amp;gt;total_ttc       += isset($this-&amp;gt;revenuestamp)?$this-&amp;gt;revenuestamp:0;&lt;br /&gt;
&lt;br /&gt;
$this-&amp;gt;db-&amp;gt;free($resql);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is the end of the loop that is summing each product/service line price to compute the total price. Just after this loop, you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Now update global field total_ht, total_ttc and tva&lt;br /&gt;
$fieldht=&#039;total_ht&#039;;&lt;br /&gt;
$fieldtva=&#039;tva&#039;;&lt;br /&gt;
$fieldlocaltax1=&#039;localtax1&#039;;&lt;br /&gt;
$fieldlocaltax2=&#039;localtax2&#039;;&lt;br /&gt;
$fieldttc=&#039;total_ttc&#039;;&lt;br /&gt;
// Specific code for backward compatibility with old field names&lt;br /&gt;
if ($this-&amp;gt;element == &#039;facture&#039; || $this-&amp;gt;element == &#039;facturerec&#039;)             $fieldht=&#039;total&#039;;&lt;br /&gt;
if ($this-&amp;gt;element == &#039;facture_fourn&#039; || $this-&amp;gt;element == &#039;invoice_supplier&#039;) $fieldtva=&#039;total_tva&#039;;&lt;br /&gt;
if ($this-&amp;gt;element == &#039;propal&#039;)                                                $fieldttc=&#039;total&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Between those two blocks of code (so just above // Now update global field total_ht, total_ttc and tva), you can place the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
if (!empty($cf_parent_record-&amp;gt;global_coefficient)) { // avoid applying the coefficient if empty (this will return a 0 price)&lt;br /&gt;
    $this-&amp;gt;total_ht = $this-&amp;gt;total_ht * $cf_parent_record-&amp;gt;global_coefficient;&lt;br /&gt;
    $this-&amp;gt;total_tva = $this-&amp;gt;total_tva * $cf_parent_record-&amp;gt;global_coefficient;&lt;br /&gt;
    $this-&amp;gt;total_ttc = $this-&amp;gt;total_ttc * $cf_parent_record-&amp;gt;global_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Done, now try to add/edit a product/service line, and the total price should be affected by the global coefficient.&lt;br /&gt;
&lt;br /&gt;
Note that this modification &#039;&#039;&#039;needs only to be done once for all modules&#039;&#039;&#039;, you then just have to create a custom field named &amp;quot;global_coefficient&amp;quot; in every module you want to apply this coefficient.&lt;br /&gt;
&lt;br /&gt;
== Implementing the change of price in margin module ==&lt;br /&gt;
&lt;br /&gt;
If you enabled the margin module, you should see that the coefficient is not applied there, but it&#039;s easy to fix that.&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/class/commonobject.class.php and find the function getMarginInfos(), then inside find the following lines:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach($this-&amp;gt;lines as $line) {&lt;br /&gt;
    if (empty($line-&amp;gt;pa_ht) &amp;amp;&amp;amp; isset($line-&amp;gt;fk_fournprice) &amp;amp;&amp;amp; !$force_price) {&lt;br /&gt;
        $product = new ProductFournisseur($this-&amp;gt;db);&lt;br /&gt;
        if ($product-&amp;gt;fetch_product_fournisseur_price($line-&amp;gt;fk_fournprice))&lt;br /&gt;
            $line-&amp;gt;pa_ht = $product-&amp;gt;fourn_unitprice * (1 - $product-&amp;gt;fourn_remise_percent / 100);&lt;br /&gt;
        if (isset($conf-&amp;gt;global-&amp;gt;MARGIN_TYPE) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;MARGIN_TYPE == &amp;quot;2&amp;quot; &amp;amp;&amp;amp; $product-&amp;gt;fourn_unitcharges &amp;gt; 0)&lt;br /&gt;
            $line-&amp;gt;pa_ht += $product-&amp;gt;fourn_unitcharges;&lt;br /&gt;
    }&lt;br /&gt;
    // si prix d&#039;achat non renseigné et devrait l&#039;être, alors prix achat = prix vente&lt;br /&gt;
    if ((!isset($line-&amp;gt;pa_ht) || $line-&amp;gt;pa_ht == 0) &amp;amp;&amp;amp; $line-&amp;gt;subprice &amp;gt; 0 &amp;amp;&amp;amp; (isset($conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull == 1)) {&lt;br /&gt;
        $line-&amp;gt;pa_ht = $line-&amp;gt;subprice * (1 - ($line-&amp;gt;remise_percent / 100));&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the lines are filled with margin infos. The price has just been fetched here, and we will now modify it with the coefficient before the price gets used for margin computation.&lt;br /&gt;
&lt;br /&gt;
To optimize things up, we will load the custom fields before the loop (because they don&#039;t change between product/service lines, they are the custom fields of the parent object), and then apply the global_coefficient inside the loop.&lt;br /&gt;
&lt;br /&gt;
Just above the code we found (just before the for loop), paste the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
// Get object&#039;s id (either stored in property &amp;quot;id&amp;quot; or &amp;quot;rowid&amp;quot;)&lt;br /&gt;
$this_id = (!empty($this-&amp;gt;id)) ? $this-&amp;gt;id : $this-&amp;gt;rowid;&lt;br /&gt;
// Load custom fields records&lt;br /&gt;
$customfields = customfields_fill_object($this);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And now, just below the code we found, inside the loop (and just above the comment: &amp;quot;// calcul des marges&amp;quot;), paste the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
// Apply the coefficient&lt;br /&gt;
if (!empty($this-&amp;gt;customfields-&amp;gt;cf_global_coefficient)) {&lt;br /&gt;
    $line-&amp;gt;pa_ht = $line-&amp;gt;pa_ht * $this-&amp;gt;customfields-&amp;gt;cf_global_coefficient;&lt;br /&gt;
    $line-&amp;gt;subprice = $line-&amp;gt;subprice * $this-&amp;gt;customfields-&amp;gt;cf_global_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that here we cheated a bit: we do not compute the coefficient on the global total price, but rather per product/service line price. This is done on purpose, because mathematically this should produce no difference, and it also allows Dolibarr to continue peacefully with its margin calculations (else we would have to adapt all those computations, which would imply a lot more of code changes!).&lt;br /&gt;
&lt;br /&gt;
That&#039;s all, the price should be reflected in the margin. Just refresh the page to reflect the change.&lt;br /&gt;
&lt;br /&gt;
Note that this modification &#039;&#039;&#039;needs only to be done once for all modules&#039;&#039;&#039;, you then just have to create a custom field named &amp;quot;global_coefficient&amp;quot; in every module you want to apply this coefficient.&lt;br /&gt;
&lt;br /&gt;
== Refresh the price instantly ==&lt;br /&gt;
&lt;br /&gt;
Maybe you noticed one glitch: when you modify the global_coefficient, the global total price isn&#039;t updated until you add/edit a product/service line. This is because the update_price() function is only called when we add/edit a product/service line.&lt;br /&gt;
&lt;br /&gt;
To fix that, we need now to force the refreshing of price. To do that, we will use an &amp;quot;aftersave&amp;quot; overloading function, which will execute commands after our custom field is saved.&lt;br /&gt;
&lt;br /&gt;
Go to the folder /htdocs/customfields/fields and rename the file customfields_fields_extend.lib.php.example into customfields_fields_extend.lib.php .&lt;br /&gt;
&lt;br /&gt;
Now, open it in your favourite text editor, and add the following function :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_aftersave_facture_global_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value, $fields) {&lt;br /&gt;
    // Force refreshing total price&lt;br /&gt;
    $object-&amp;gt;update_price(0, &#039;auto&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s all. Now, when you edit your global_coefficient, this will force refresh the total price.&lt;br /&gt;
&lt;br /&gt;
However, you will still notice one last glitch: when you edit the global_coefficient, the total price isn&#039;t reflected instantly, you must refresh the webpage once to see the new price. This is because of how the datasheet and the CustomFields hook are designed: CustomFields is called only exactly where the custom fields are place, thus it is called after the total price, the margin and the remaining debt are printed. Thus, CustomFields updates the value of your custom field only after all those informations get printed, and thus it cannot modify them at this stage. That&#039;s why those information are out-of-date, and you must refresh the webpage once to get to see the latest values.&lt;br /&gt;
&lt;br /&gt;
There is a workaround for this: refresh automatically the webpage (but delete the POST data to avoid an infinite loop). You just need to use this overloading code instead of the one above:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_aftersave_facture_global_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value, $fields) {&lt;br /&gt;
    // Force refreshing total price&lt;br /&gt;
    $object-&amp;gt;update_price(0, &#039;auto&#039;);&lt;br /&gt;
    // Force refresh the page to update prices that were printed before the custom field aftersave was triggered (and only if not creating the object because then there&#039;s no need to refresh)&lt;br /&gt;
    if ($action !== &#039;add&#039;) print(&#039;&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;window.location.href = window.location.pathname + window.location.search;&amp;lt;/script&amp;gt;&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Another way to fix this issue would be to modify how CustomFields save the custom fields, by moving this stage at the very top. However, this would need another hook, which should be called at the very beginning of the page loading, which should be made only for this purpose (PageLoading), and should be available in all modules supported by CustomFields in order to not break anything.&lt;br /&gt;
&lt;br /&gt;
Note that you will need to create one overloading function for each module you want to support with the global_coefficient custom field. For example, if you want to support both invoices and commercial proposals, you will need two overloading functions:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient for invoices (note the &amp;quot;facture&amp;quot; in the name of the function)&lt;br /&gt;
function customfields_field_aftersave_facture_global_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value, $fields) {&lt;br /&gt;
    // Force refreshing total price&lt;br /&gt;
    $object-&amp;gt;update_price(0, &#039;auto&#039;);&lt;br /&gt;
    // Force refresh the page to update prices that were printed before the custom field aftersave was triggered (and only if not creating the object because then there&#039;s no need to refresh)&lt;br /&gt;
    if ($action !== &#039;add&#039;) print(&#039;&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;window.location.href = window.location.pathname + window.location.search;&amp;lt;/script&amp;gt;&#039;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Global coefficient for commercial proposals (note the &amp;quot;propal&amp;quot; in the name of the function)&lt;br /&gt;
function customfields_field_aftersave_propal_global_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value, $fields) {&lt;br /&gt;
    // Force refreshing total price&lt;br /&gt;
    $object-&amp;gt;update_price(0, &#039;auto&#039;);&lt;br /&gt;
    // Force refresh the page to update prices that were printed before the custom field aftersave was triggered (and only if not creating the object because then there&#039;s no need to refresh)&lt;br /&gt;
    if ($action !== &#039;add&#039;) print(&#039;&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;window.location.href = window.location.pathname + window.location.search;&amp;lt;/script&amp;gt;&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Modify the total price PER product/service line with a coefficient (without tampering the discount) =&lt;br /&gt;
&lt;br /&gt;
Here we will go through the creation and management of a custom field called &#039;&#039;&#039;coefficient&#039;&#039;&#039; that will change the total price of each product by modifying it by the coefficient.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will not create a coefficient per product but rather per product&#039;s line. Thus if you want to create a coefficient that never change between invoices but only depending on the product and stays static, please refer to the next chapter below about creating a custom tax per product.&lt;br /&gt;
&lt;br /&gt;
The preliminary and necessary step is to first create a custom field that will be used to modify the total price. We will create one for Client Invoice Lines.&lt;br /&gt;
&lt;br /&gt;
Go to the CustomFields administration panel, then into the Client Invoice Lines tab, then click on the button New Field, name the field &#039;&#039;&#039;coefficient&#039;&#039;&#039;, and set the type to &#039;&#039;&#039;Double&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
When you go to client invoices in Dolibarr, you should then see something similar to this:&lt;br /&gt;
[[File:Cfe1.jpg]]&lt;br /&gt;
You can see that our coefficient field was successfully created.&lt;br /&gt;
&lt;br /&gt;
Then here are two alternative ways of implementing the change of the total price of each product relativery to this coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change total price in database ==&lt;br /&gt;
&lt;br /&gt;
Here we will use a &amp;quot;save&amp;quot; overloading function to change the total price directly in the database, thus we won&#039;t have to modify anything else (eg: nor PDF templating nor ODT) since the total price will be already updated in the database.&lt;br /&gt;
&lt;br /&gt;
Warning: this method will work only with CustomFields =&amp;gt; 3.2.23 since the &amp;quot;save&amp;quot; overloading function did not work for products lines before.&lt;br /&gt;
&lt;br /&gt;
Just add the following overloading function in your customfields_fields_extend.lib.php file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_facturedet_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
 &lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $coefficient = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;coefficient&#039;}; // you could also use $coefficient = $value;&lt;br /&gt;
 &lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $coefficient = price2num($coefficient);&lt;br /&gt;
 &lt;br /&gt;
    // Get the original subprices (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
 &lt;br /&gt;
    // Update the subprices with the coefficient&lt;br /&gt;
    $object-&amp;gt;total_ht = $object-&amp;gt;total_ht * $coefficient; // compute no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva = $object-&amp;gt;total_tva * $coefficient; // compute tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc * $coefficient; // compute tax included price&lt;br /&gt;
 &lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(&#039;&#039;,true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
 &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;coefficient&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should show the correct total price on the Dolibarr&#039;s interface without modifying the unit subprice (P.U. Unit):&lt;br /&gt;
&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
You have nothing to edit in your PDF or ODT template, since the new subprice with applied coefficient will be automatically printed instead of the old one without coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change printed total price per product without tampering the subprice in the database ==&lt;br /&gt;
&lt;br /&gt;
Here is a way to change the printed total price (the one shown on screen and in PDF/ODT) but without changing the total price in the database.&lt;br /&gt;
&lt;br /&gt;
The following sub-chapters are all independent, thus you can only follow the ones that you require, eg: if you only need to change the price in your PDF documents, just create a custom field then jump to the sub-chapter to &#039;&#039;&#039;Implement the change of price in PDF&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in ODT templates ===&lt;br /&gt;
&lt;br /&gt;
There&#039;s no eval implementation currently inside ODT templates, but there is a kind of hook implementation, called [[Create_an_ODT_document_template#Other_personalized_tags|substitution functions]].&lt;br /&gt;
&lt;br /&gt;
You can either create your own module, then create your own substitution function, but for the sake of simplicity in this tutorial, we will directly use the CustomFields substitution function. However, please remember that you will have to redo your changes everytime you will update the CustomFields module, so if you don&#039;t want to do that it would be better if you create your own module and substitution function.&lt;br /&gt;
&lt;br /&gt;
Open the file &#039;&#039;&#039;htdocs/customfields/core/substitutions/functions_customfields.lib.php&#039;&#039;&#039; and go to the function &#039;&#039;&#039;customfields_completesubstitutionarray_lines&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
At the very end of this function, but inside the customfields check:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) {&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
But just before the ending bracket, you should add the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($object-&amp;gt;table_element_line === &#039;facturedet&#039;) { // First check that we are in the right module, we don&#039;t want to use coefficient for other modules if we didn&#039;t define the custom field there&lt;br /&gt;
    $substitutionarray[&#039;line_price_full&#039;] = $substitutionarray[&#039;line_price_ttc&#039;] * $substitutionarray[&#039;cf_coefficient&#039;]; // multiply the total price with our coefficient&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code will add a new tag &#039;&#039;&#039;{line_price_full}&#039;&#039;&#039; that you can then use inside your ODT templates.&lt;br /&gt;
&lt;br /&gt;
Just place this tag in a table in your ODT document, between the table tags ( [!-- BEGIN row.lines --] ... [!-- END row.lines --] ), and this will print your full price.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in PDF templates ===&lt;br /&gt;
&lt;br /&gt;
First, we will create our own PDF template by copying an existing template, and then we will edit it to change the price of items.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Creating your own PDF template by copying an existing template&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Go into the following folder: &#039;&#039;&#039;htdocs/core/modules/facture/doc/&#039;&#039;&#039;, then copy the file &#039;&#039;&#039;pdf_crabe.modules.php&#039;&#039;&#039; and name the new one &#039;&#039;&#039;pdf_crabecoeff.modules.php&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Then open it in your favourite editor, and change the following lines:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures // change pdf_crabe into pdf_crabecoeff&lt;br /&gt;
{&lt;br /&gt;
...&lt;br /&gt;
    function __construct($db)&lt;br /&gt;
    {&lt;br /&gt;
        ...&lt;br /&gt;
	$this-&amp;gt;name = &amp;quot;crabe&amp;quot;; // change &amp;quot;crabe&amp;quot; into &amp;quot;crabecoeff&amp;quot;&lt;br /&gt;
        ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you have your own PDF template!&lt;br /&gt;
&lt;br /&gt;
Now you should &#039;&#039;&#039;enable your PDF template&#039;&#039;&#039; before doing anything else, by going into the Modules Administration panel, and into the Invoice admin page, then click on the ON button at the right of the &#039;&#039;&#039;crabecoeff&#039;&#039;&#039; entry to enable the template.&lt;br /&gt;
&lt;br /&gt;
Now, let&#039;s proceed onto modifying the prices.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Changing the prices automatically in your PDF&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
You can now do pretty much anything you want in your PDF template, but I will give you a mean to automatically change the prices of every products with our coefficient, as well as the total price, without editing too much the template.&lt;br /&gt;
&lt;br /&gt;
First, try to find the following code of line (should be inside the write_file() method):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf=pdf_getInstance($this-&amp;gt;format);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, just copy/paste the following code &#039;&#039;&#039;below&#039;&#039;&#039; pdf_getInstance():&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$this-&amp;gt;customfields = customfields_fill_object($object, null, $outputlangs, null, true); // beautified values&lt;br /&gt;
$this-&amp;gt;customfields_raw = customfields_fill_object($object, null, $outputlangs, &#039;raw&#039;, null); // raw values&lt;br /&gt;
$this-&amp;gt;customfields_lines = customfields_fill_object_lines($object, null, $outputlangs, null, true); // product lines&#039; values&lt;br /&gt;
&lt;br /&gt;
// Resetting every $object price because we will compute them anew&lt;br /&gt;
$object-&amp;gt;total_ht = 0;&lt;br /&gt;
$object-&amp;gt;total_tva = 0;&lt;br /&gt;
$object-&amp;gt;total_ttc = 0;&lt;br /&gt;
// For each line of product/service, we will recompute all the prices&lt;br /&gt;
foreach($object-&amp;gt;lines as $line) {&lt;br /&gt;
    // Get the coefficient custom field&#039;s value for the current line&lt;br /&gt;
    $coeff = $object-&amp;gt;customfields-&amp;gt;lines-&amp;gt;{$line-&amp;gt;rowid}-&amp;gt;cf_coefficient;&lt;br /&gt;
    // Recompute the price of this product/service line&lt;br /&gt;
    $line-&amp;gt;total_ht = $line-&amp;gt;total_ht*$coeff; // compute no tax price&lt;br /&gt;
    $line-&amp;gt;total_tva = $line-&amp;gt;total_tva*$coeff; // compute tax amount&lt;br /&gt;
    $line-&amp;gt;total_ttc = $line-&amp;gt;total_ttc*$coeff; // compute tax included price&lt;br /&gt;
    // Do the sum of all new products/services prices and tva (will be incremented with every product/service line)&lt;br /&gt;
    $object-&amp;gt;total_ht += $line-&amp;gt;total_ht; // global no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva += $line-&amp;gt;total_tva; // global vat/tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc += $line-&amp;gt;total_ttc; // global  tax included price&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you should try to generate your PDF document, and you should get something like this:&lt;br /&gt;
[[File:Cfe3.jpg]]&lt;br /&gt;
&lt;br /&gt;
Note: printing the coefficient as a column in your PDF document is also possible but will not be covered here because PDF templates will vary for every persons depending on your needs.&lt;br /&gt;
For guidelines about how to do this, you can read the documentation on [[Create_a_PDF_document_template|how to create a PDF template]] and [[Module_CustomFields#Implementing_in_PDF_templates|how to use custom fields in PDF templates]].&lt;br /&gt;
&lt;br /&gt;
Hint: you should be looking inside the following conditional block, inside the write_file() method:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
{&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of total price in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
Unluckily there is not (yet) any hooking context to modify how lines of products/services are printed, thus the only thing you can do is directly edit the &#039;&#039;&#039;template file&#039;&#039;&#039; and manually put inside your code to load your custom field and print it the way you want using HTML formatting, for our example:&lt;br /&gt;
&lt;br /&gt;
* For Dolibarr 3.3 and above, edit the file &#039;&#039;&#039;htdocs/core/tpl/objectline_view.tpl.php&#039;&#039;&#039;&lt;br /&gt;
* For Dolibarr 3.1 and 3.2, edit the file &#039;&#039;&#039;htdocs/core/tpl/freeproductline_view.tpl.php&#039;&#039;&#039; AND &#039;&#039;&#039;predefinedproductline_view.tpl.php&#039;&#039;&#039; (apply the same changes to both files)&lt;br /&gt;
&lt;br /&gt;
In the template file you opened, find the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And replace it by:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $object-&amp;gt;table_element_line; // you can use $object-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
&lt;br /&gt;
// Modifying the HT price and printing it on the Dolibarr&#039;s datasheet interface&lt;br /&gt;
// Applying a multiplying/dividing coefficient to an HT price will give a mathematically correct TTC price since the taxes are also a multiplier&lt;br /&gt;
// However, if you want to apply other mathematical operations (like addition or substaction), you should use the total price with $line-&amp;gt;total_ttc&lt;br /&gt;
if (!empty($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
    echo price($line-&amp;gt;total_ht * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient);&lt;br /&gt;
} else {&lt;br /&gt;
    echo price($line-&amp;gt;total_ht);&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should produce something like this:&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Technical note: here we are using customfields_fill_object() instead of customfields_fill_object_lines() (the latter being specifically adapted to lines and thus should theoretically be used here) for performance reasons because the template file we are editing is called in a loop, and customfields_fill_object_lines() should be called outside a loop (because it already does the job that is done by the loop), so here we &#039;tricks&#039; the customfields_fill_object() function to avoid this loop by submitting &#039;facturedet&#039; as a module by itself (when in fact it&#039;s a product lines table, but it works the same way nevertheless). This is a good demonstration of the flexibility of the CustomFields functions and methods.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the coefficient column in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
It&#039;s also possible to show the coefficient in a new column on the Dolibarr interface.&lt;br /&gt;
&lt;br /&gt;
To do that, you need to do two things:&lt;br /&gt;
&lt;br /&gt;
1- Add the column title &amp;quot;Coeff.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
2- Add the generator that will input the coefficient value for each line.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Add the column title &amp;quot;Coeff.&amp;quot;&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
Open in your favourite editor the file /htdocs/core/class/commonobject.class.php and inside the function printObjectLines (note the plural s), find the following lines of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Total HT&lt;br /&gt;
print &#039;&amp;lt;td align=&amp;quot;right&amp;quot; width=&amp;quot;50&amp;quot;&amp;gt;&#039;.$langs-&amp;gt;trans(&#039;TotalHTShort&#039;).&#039;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is what prints the Total HT price column. We will place the Coeff column just before.&lt;br /&gt;
&lt;br /&gt;
So, just above this code, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
dol_include_once(&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element_line);&lt;br /&gt;
$coeff_field = $customfields-&amp;gt;fetchFieldStruct(&#039;coefficient&#039;);&lt;br /&gt;
if (!empty($coeff_field)) {&lt;br /&gt;
    print &#039;&amp;lt;td align=&amp;quot;right&amp;quot; width=&amp;quot;50&amp;quot;&amp;gt;&#039;.$langs-&amp;gt;trans(&#039;Coeff.&#039;).&#039;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will take care of showing the Coeff. column, but only for modules where you created such a custom field (because the file we modified is a common object for all modules in Dolibarr, not just invoices, thus this modification will gracefully skip this custom field if it does not exists for other modules).&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Add the generator&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/tpl/objectline_view.tpl.php and try to find the following block of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php if ($line-&amp;gt;special_code == 3)	{ ?&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo $langs-&amp;gt;trans(&#039;Option&#039;); ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } else { ?&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is what prints the total HT price value for each line.&lt;br /&gt;
&lt;br /&gt;
Just above of this block of code, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $object-&amp;gt;table_element_line; // you can use $this-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
if (isset($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
    &amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient; ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just like for the column title, this code will take care of generating the coefficient value only if the custom field is defined, because this file is also a generic template for all modules of Dolibarr.&lt;br /&gt;
&lt;br /&gt;
At this point, everything is done, you should see the following:&lt;br /&gt;
[[File:Cf_wiki_coeff_column.png]]&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in margin module ===&lt;br /&gt;
&lt;br /&gt;
If you enabled the margin module, you should see that the coefficient is not applied there, but it&#039;s easy to fix that.&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/class/commonobject.class.php and find the function getMarginInfos(), then inside find the following lines:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach($this-&amp;gt;lines as $line) {&lt;br /&gt;
    if (empty($line-&amp;gt;pa_ht) &amp;amp;&amp;amp; isset($line-&amp;gt;fk_fournprice) &amp;amp;&amp;amp; !$force_price) {&lt;br /&gt;
        $product = new ProductFournisseur($this-&amp;gt;db);&lt;br /&gt;
        if ($product-&amp;gt;fetch_product_fournisseur_price($line-&amp;gt;fk_fournprice))&lt;br /&gt;
            $line-&amp;gt;pa_ht = $product-&amp;gt;fourn_unitprice * (1 - $product-&amp;gt;fourn_remise_percent / 100);&lt;br /&gt;
        if (isset($conf-&amp;gt;global-&amp;gt;MARGIN_TYPE) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;MARGIN_TYPE == &amp;quot;2&amp;quot; &amp;amp;&amp;amp; $product-&amp;gt;fourn_unitcharges &amp;gt; 0)&lt;br /&gt;
            $line-&amp;gt;pa_ht += $product-&amp;gt;fourn_unitcharges;&lt;br /&gt;
    }&lt;br /&gt;
    // si prix d&#039;achat non renseigné et devrait l&#039;être, alors prix achat = prix vente&lt;br /&gt;
    if ((!isset($line-&amp;gt;pa_ht) || $line-&amp;gt;pa_ht == 0) &amp;amp;&amp;amp; $line-&amp;gt;subprice &amp;gt; 0 &amp;amp;&amp;amp; (isset($conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull == 1)) {&lt;br /&gt;
        $line-&amp;gt;pa_ht = $line-&amp;gt;subprice * (1 - ($line-&amp;gt;remise_percent / 100));&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the lines are filled with margin infos. The price has just been fetched here, and we will now modify it with the coefficient before the price gets used for margin computation.&lt;br /&gt;
&lt;br /&gt;
Just below the code we found, paste the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $line-&amp;gt;table_element;&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
if (!empty($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
    $line-&amp;gt;pa_ht = $line-&amp;gt;pa_ht * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient;&lt;br /&gt;
    $line-&amp;gt;subprice = $line-&amp;gt;subprice * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s all, the price should be reflected in the margin.&lt;br /&gt;
[[File:Cf_wiki_coeff_margin.png]]&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in remaining debt to pay ===&lt;br /&gt;
&lt;br /&gt;
Open /htdocs/core/class/commonobject.class.php and look for the function update_price().&lt;br /&gt;
&lt;br /&gt;
You should see the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function update_price($exclspec=0,$roundingadjust=&#039;none&#039;,$nodatabaseupdate=0,$seller=&#039;&#039;)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just below the include_once line, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;;&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/conf/conf_customfields_func.lib.php&#039;;&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $this-&amp;gt;table_element_line);&lt;br /&gt;
// Fetching line ids from parent object id&lt;br /&gt;
$line_ids = $customfields-&amp;gt;fetchAny(&#039;rowid&#039;, MAIN_DB_PREFIX.$this-&amp;gt;table_element_line, $this-&amp;gt;fk_element.&#039; = &#039;.$this-&amp;gt;id);&lt;br /&gt;
$line_ids = array_values_recursive(&#039;rowid&#039;, $line_ids);&lt;br /&gt;
// Load custom fields&lt;br /&gt;
$cf_records = $customfields-&amp;gt;fetch($line_ids);&lt;br /&gt;
// Reassociate the records with the line id (instead of the customfields record id)&lt;br /&gt;
$cf_line_cfrowid_to_rowid = array_reassociate(&#039;rowid&#039;, &#039;fk_&#039;.$this-&amp;gt;table_element_line, $cf_records);&lt;br /&gt;
$tmp = $cf_records;&lt;br /&gt;
$cf_records = array();&lt;br /&gt;
foreach ($tmp as $k=&amp;gt;$v) { $cf_records[$cf_line_cfrowid_to_rowid[$k]] = $tmp[$k]; }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will load the coefficient custom field (and also any other custom field you have defined for lines).&lt;br /&gt;
&lt;br /&gt;
Now scroll below, and you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
while ($i &amp;lt; $num)&lt;br /&gt;
{&lt;br /&gt;
    $obj = $this-&amp;gt;db-&amp;gt;fetch_object($resql);&lt;br /&gt;
&lt;br /&gt;
    // Note: There is no check on detail line and no check on total, if $forcedroundingmode = &#039;none&#039;&lt;br /&gt;
&lt;br /&gt;
    if ($forcedroundingmode == &#039;0&#039;)	// Check if data on line are consistent. This may solve lines that were not consistent because set with $forcedroundingmode=&#039;auto&#039;&lt;br /&gt;
    {&lt;br /&gt;
        $localtax_array=array($obj-&amp;gt;localtax1_type,$obj-&amp;gt;localtax1_tx,$obj-&amp;gt;localtax2_type,$obj-&amp;gt;localtax2_tx);&lt;br /&gt;
        $tmpcal=calcul_price_total($obj-&amp;gt;qty, $obj-&amp;gt;up, $obj-&amp;gt;remise_percent, $obj-&amp;gt;vatrate, $obj-&amp;gt;localtax1_tx, $obj-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $obj-&amp;gt;info_bits, $obj-&amp;gt;product_type, $seller, $localtax_array);&lt;br /&gt;
        $diff=price2num($tmpcal[1] - $obj-&amp;gt;total_tva, &#039;MT&#039;, 1);&lt;br /&gt;
        if ($diff)&lt;br /&gt;
        {&lt;br /&gt;
            $sqlfix=&amp;quot;UPDATE &amp;quot;.MAIN_DB_PREFIX.$this-&amp;gt;table_element_line.&amp;quot; SET &amp;quot;.$fieldtva.&amp;quot; = &amp;quot;.$tmpcal[1].&amp;quot;, total_ttc = &amp;quot;.$tmpcal[2].&amp;quot; WHERE rowid = &amp;quot;.$obj-&amp;gt;rowid;&lt;br /&gt;
            dol_syslog(&#039;We found unconsistent data into detailed line (difference of &#039;.$diff.&#039;) for line rowid = &#039;.$obj-&amp;gt;rowid.&amp;quot; (total vat of line calculated=&amp;quot;.$tmpcal[1].&amp;quot;, database=&amp;quot;.$obj-&amp;gt;total_tva.&amp;quot;). We fix the total_vat and total_ttc of line by running sqlfix = &amp;quot;.$sqlfix);&lt;br /&gt;
            $resqlfix=$this-&amp;gt;db-&amp;gt;query($sqlfix);&lt;br /&gt;
            if (! $resqlfix) dol_print_error($this-&amp;gt;db,&#039;Failed to update line&#039;);&lt;br /&gt;
            $obj-&amp;gt;total_tva = $tmpcal[1];&lt;br /&gt;
            $obj-&amp;gt;total_ttc = $tmpcal[2];&lt;br /&gt;
            //&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just after, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
$cf_coeff = $cf_records[$obj-&amp;gt;rowid]-&amp;gt;coefficient;&lt;br /&gt;
if (empty($cf_coeff) and $cf_coeff !== 0) $cf_coeff = 1; // by default, an empty coefficient = 1&lt;br /&gt;
$obj-&amp;gt;total_ht = $obj-&amp;gt;total_ht * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_tva = $obj-&amp;gt;total_tva * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_localtax1 = $obj-&amp;gt;total_localtax1 * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_localtax2 = $obj-&amp;gt;total_localtax2 * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_ttc = $obj-&amp;gt;total_ttc * $cf_coeff;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should do the trick. Now go to one of your invoices (or whatever module you are targetting), and try to add/edit a product/service line to force refresh the total price. You should then see this:&lt;br /&gt;
[[File:Cf_wiki_coeff_remaining_debt.png]]&lt;br /&gt;
&lt;br /&gt;
= Custom tax per product =&lt;br /&gt;
&lt;br /&gt;
This part will show you how to create a static custom tax per product. Static means that the tax will be stored per product, and not per product&#039;s line, thus you just have to setup the tax once per product and it will always be applied in every invoice you add this product.&lt;br /&gt;
&lt;br /&gt;
To do that, you will need the CustomFields Pro module + ProductsEasyAccess.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
1- Create a product&#039;s custom fields called &amp;quot;custom_tax&amp;quot;. Now, using ProductsEasyAccess, you can access this field wherever you wants, either to show it on the Dolibarr&#039;s interface (see previous chapter), or in a PDF template, which we will describe below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2- Modify your invoice&#039;s PDF template. You first need to load all the products&#039; fields &#039;&#039;before&#039;&#039; the loop over the products&#039; lines. To do this, search for a line like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// This should be placed above the &amp;quot;loop lines&amp;quot; below, add this:&lt;br /&gt;
&lt;br /&gt;
dol_include_once(&#039;/productseasyaccess/lib/productseasyaccess.lib.php&#039;);&lt;br /&gt;
fill_object_with_products_fields($object);&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines, search for this command&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  // Add this just below the for loop, this will ease your php manipulation after.&lt;br /&gt;
  // This will allow you to more easily access the products&#039; fields (this will store the id of each product inside $lineproductid).&lt;br /&gt;
  $line_product_id = $object-&amp;gt;lines[$i]-&amp;gt;fk_product;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4- You can now use your products&#039; fields in your PDF. To access the custom_tax field, just use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And to print it in your PDF:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell(0,3, $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax, 0, &#039;L&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5- Then, if you want to count the custom tax total and add it to your vat total, you can create a counter inside the for loop we cited in the first step:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_custom_tax = 0; // Initialize the custom tax total counter&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  $object-&amp;gt;total_custom_tax += $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then find inside the function **_tableau_tot**, find the following line:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And prepend the total modification like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc + $object-&amp;gt;total_custom_tax; // prepend this to add the custom tax to the total&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Done. This should show your custom tax per product and your total including all custom taxes.&lt;/div&gt;</summary>
		<author><name>Lrq3000</name></author>
	</entry>
	<entry>
		<id>https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33079</id>
		<title>Module CustomFields Cases</title>
		<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33079"/>
		<updated>2015-01-16T13:41:12Z</updated>

		<summary type="html">&lt;p&gt;Lrq3000: /* Refresh the price instantly */ Add javascript instant refresh code + more infos about commercial proposals&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:CustomFields]]&lt;br /&gt;
{{TemplateDocDevEn}}&lt;br /&gt;
{{TemplateModEN}}&lt;br /&gt;
{{BasculeDevUserEn|&lt;br /&gt;
name=CustomFields|&lt;br /&gt;
num=8500|&lt;br /&gt;
devdoc=[[Module_CustomFields_Dev]]|&lt;br /&gt;
userdoc=[[Module_CustomFields]]|}}&lt;br /&gt;
This page lists a compilation of concrete, practical examples uses of [[Module_CustomFields|CustomFields]] and of its various features.&lt;br /&gt;
&lt;br /&gt;
If you had an experience with CustomFields, you can also add here your case as an example for others to get inspired.&lt;br /&gt;
&lt;br /&gt;
= How to make a generic PDF template listing all custom fields =&lt;br /&gt;
&lt;br /&gt;
To make a generic PDF template listing all custom fields, you just need to take an existing PDF template, then you need to follow these 3 steps:&lt;br /&gt;
&lt;br /&gt;
1/ Edit the class name (required, else it will conflict with the original template).&lt;br /&gt;
&lt;br /&gt;
Eg: if we take the crabe template for invoices, we change:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;crabe&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
into:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_customfields extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;customfields&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2/ Copy this generic function at the end of the PDF:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propale/order/etc... (CustomFields simpler functions will automatically adapt)&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs); // set default PDF font size&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
    // Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
    $customfields = customfields_fill_object($object, null, $outputlangs, null, true);&lt;br /&gt;
&lt;br /&gt;
    // Setting the starting position of the text cursor&lt;br /&gt;
    $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
    $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
    // Printing the customfields&lt;br /&gt;
    foreach ($object-&amp;gt;customfields as $label=&amp;gt;$value) { // $value is already formatted!&lt;br /&gt;
        // Get translated label&lt;br /&gt;
        $translatedlabel = $customfields-&amp;gt;findLabelPDF($label, $outputlangs); // translated label of the customfield (not translated by default in customfields_fill_object() because a field should always be accessible by a base name, whatever the translation is)&lt;br /&gt;
&lt;br /&gt;
        // PDF formatting, placement and printing&lt;br /&gt;
        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
        $pdf-&amp;gt;MultiCell(0,3, $translatedlabel.&#039; ($object-&amp;gt;customfields-&amp;gt;&#039;.$label.&#039;): &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternative function (with a bit less functionalities, but more controllable):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page (used to debug if CustomFields setup is correct)&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propal/product/whatever...&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs);&lt;br /&gt;
&lt;br /&gt;
            if (empty($object-&amp;gt;table_element) or empty($object-&amp;gt;id)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;Current \$object is not compatible with CustomFields, could not find table_element or id.&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
    $customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element);&lt;br /&gt;
&lt;br /&gt;
            // Fetching custom fields records&lt;br /&gt;
            $fields = $customfields-&amp;gt;fetch($object-&amp;gt;id);&lt;br /&gt;
&lt;br /&gt;
            if (!isset($fields)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;No custom field could be found for this object. Please check your configuration (did you create at least one customfield and set a value in the current datasheet?)&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            } else {&lt;br /&gt;
                // Setting the starting position of the text cursor&lt;br /&gt;
                $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
                $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
                // Printing the customfields&lt;br /&gt;
                foreach ($fields as $key=&amp;gt;$field) {&lt;br /&gt;
                        $translatedname = $customfields-&amp;gt;findLabelPDF($key, $outputlangs); // label of the customfield&lt;br /&gt;
                        $value = $customfields-&amp;gt;simpleprintFieldPDF($key, $field, $outputlangs); // value (cleaned and properly formatted) of the customfield&lt;br /&gt;
&lt;br /&gt;
                        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
                        $pdf-&amp;gt;MultiCell(0,3, $translatedname.&#039;: &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
                        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3/ Call the _pagecustomfields function at the right place (generally, after _pagefoot and just before $pdf-&amp;gt;Close()):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Pied de page&lt;br /&gt;
$this-&amp;gt;_pagefoot($pdf,$object,$outputlangs);&lt;br /&gt;
$pdf-&amp;gt;AliasNbPages();&lt;br /&gt;
&lt;br /&gt;
// CustomFields&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) { // if the customfields module is activated...&lt;br /&gt;
        // Add a page with the customfields&lt;br /&gt;
        $pdf-&amp;gt;AddPage();&lt;br /&gt;
        $this-&amp;gt;_pagecustomfields($pdf,$object,$outputlangs);&lt;br /&gt;
        $pagenb++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
$pdf-&amp;gt;Close(); // you should first search for this, you should only find one occurrence&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then go to the module&#039;s admin panel (eg: invoice admin panel) and enable the template you&#039;ve just created.&lt;br /&gt;
&lt;br /&gt;
You can now use it to test if your customfields work.&lt;br /&gt;
&lt;br /&gt;
= Conditions on fields selection =&lt;br /&gt;
&lt;br /&gt;
Sometimes you may want to show a DropdownBox or Constraint (or another multi-choice type), but you may want to show or hide choices based on conditions (eg: only if it&#039;s a prospect, only if this zone was selected, only if this category was selected, etc..).&lt;br /&gt;
&lt;br /&gt;
To know which is the best solution you should choose to implement your condition, you should first know in which category your condition falls in.&lt;br /&gt;
&lt;br /&gt;
== Conditions categories ==&lt;br /&gt;
&lt;br /&gt;
Conditions can be categorized into 4 types:&lt;br /&gt;
&lt;br /&gt;
* static condition: choices are restrained on a condition that never changes (eg: only show third-parties that are suppliers, or lowercase all data on sql insertion, or check that a field is above a certain number, etc.).&lt;br /&gt;
* semi-dynamic condition: choices that are constrained on a static table but based on the value of another field/customfield. This is also commonly called &amp;quot;Cascaded dropdown lists&amp;quot; or &amp;quot;Dynamically linked dropdown lists&amp;quot;.&lt;br /&gt;
* dynamic: everything is dynamic: choices are based on a subfield of a field of the current module, so that there&#039;s no material table and it&#039;s impossible to create a view (because you would need to create one view per id) (eg: show only the contacts attached to the third-party being accessed)&lt;br /&gt;
* timed condition: a condition that is based on time or recurrence (eg: at 5:00 AM delete old records, etc..)&lt;br /&gt;
&lt;br /&gt;
== Conditions solutions ==&lt;br /&gt;
&lt;br /&gt;
Now that you know what kind of condition you want to set, you can choose one of the following solutions depending on your preferences and knowledge.&lt;br /&gt;
&lt;br /&gt;
In any case, you can always use the &#039;&#039;&#039;CustomFields&#039;s overload functions&#039;&#039;&#039; to fit your needs for nearly every type of condition.&lt;br /&gt;
&lt;br /&gt;
=== Static condition ===&lt;br /&gt;
&lt;br /&gt;
* View: create a view on the table based on the condition, and then create a custom field constrained on this view (which is just like any table), eg: only show third-parties that are suppliers:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
CREATE VIEW llx_societe_supplier_view AS&lt;br /&gt;
SELECT *&lt;br /&gt;
FROM llx_societe&lt;br /&gt;
WHERE fournisseur=1;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
WARNING: foreign keys (and thus constraints) can&#039;t be used on views with MySQL, and materialized views neither exist, so you can&#039;t use this method with MySQL.&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
* SQL Transformations&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
=== Semi-dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade option is ideal in most cases, and if not enough, you can use the Cascade Custom option and Overloading Functions.&lt;br /&gt;
* View&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
&lt;br /&gt;
=== Dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade Custom and Overload functions&lt;br /&gt;
* Making your own module, which calls the CustomFields class&lt;br /&gt;
&lt;br /&gt;
=== Timed condition ===&lt;br /&gt;
&lt;br /&gt;
* SQL scheduled events&lt;br /&gt;
* Cron job&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
= Overloading functions =&lt;br /&gt;
&lt;br /&gt;
Here are a few concrete use cases of overloading functions to achieve various goals.&lt;br /&gt;
&lt;br /&gt;
== Dynamic list selection based on another one ==&lt;br /&gt;
&lt;br /&gt;
The goal is to show two custom fields on the Third-Party module: one which gives the zone (secteur) of the third-party (Isere, Alpes du sud, Haute-Savoie...) and the other which gives relative to the zone the list of all the ski resorts (station_a) inside the selected zone.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will show how to manually manage this kind of requirements using [[Module_CustomFields#Overloading_functions:_Adding_your_own_management_code_for_custom_fields|overloading functions]], but in latest CustomFields releases, the same solution can be achieved automatically using the [[Module_CustomFields#Cascade|Cascade option]].&lt;br /&gt;
&lt;br /&gt;
We have one table &#039;&#039;&#039;llx_k_station&#039;&#039;&#039; (rowid, station (char50), secteur(char50)) which contains the following: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1,les 2 alpes,isère&lt;br /&gt;
2,chamrousse,isère&lt;br /&gt;
3,alpe d&#039;huez,isère&lt;br /&gt;
4,vars,alpes du sud&lt;br /&gt;
5,risoul,alpes du sud&lt;br /&gt;
etc...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In CustomFields&#039;s admin panel, we create two custom fields:&lt;br /&gt;
&lt;br /&gt;
1- secteur: returns the list of all zones - type DropdownBox: enum(&#039;- Aucun&#039;,&#039;Alpes du Sud&#039;,&#039;export&#039;,&#039;Haute-Savoie&#039;,&#039;Isère&#039;, etc...)&lt;br /&gt;
&lt;br /&gt;
2- station_a: returns the list of all ski resorts - type constraint on &#039;&#039;&#039;llx_k_station&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Here is the code to put in customfields_fields_extend.lib.php that will allow to show only the ski resorts corresponding to the selected zone:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_editview_societe_station_a (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$parameters, &amp;amp;$action, &amp;amp;$user, &amp;amp;$idvar, &amp;amp;$rightok, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
  global $db; // allow to access database functions&lt;br /&gt;
  $sql=&amp;quot;SELECT llx_k_station.station, llx_k_station.rowid FROM llx_societe_customfields INNER JOIN llx_k_station ON llx_societe_customfields.Secteur = llx_k_station.secteur WHERE llx_societe_customfields.fk_societe=&amp;quot;.$object-&amp;gt;id;&lt;br /&gt;
$result=$db-&amp;gt;query($sql);&lt;br /&gt;
 &lt;br /&gt;
  if ($result) {&lt;br /&gt;
    $num = $db-&amp;gt;num_rows($result);&lt;br /&gt;
    $i = 0;&lt;br /&gt;
    $myarray = array();&lt;br /&gt;
    while ($i &amp;lt; $num)&lt;br /&gt;
    {&lt;br /&gt;
      $obj = $db-&amp;gt;fetch_object($result);&lt;br /&gt;
      $myarray []=array(&#039;id&#039;=&amp;gt;$obj-&amp;gt;rowid, &#039;value&#039;=&amp;gt;$obj-&amp;gt;station);&lt;br /&gt;
      $i++;&lt;br /&gt;
    }&lt;br /&gt;
    $value = $myarray; &lt;br /&gt;
    $db-&amp;gt;free($result);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to manub for giving this case.&lt;br /&gt;
&lt;br /&gt;
== Dynamic discount computation per products lines on propales ==&lt;br /&gt;
&lt;br /&gt;
The goal here is to compute the discount for a product line in propales based on a computation on three custom fields.&lt;br /&gt;
&lt;br /&gt;
First, create three custom fields for &amp;quot;Line commercial proposals&amp;quot;: vl, cpl and lc.&lt;br /&gt;
&lt;br /&gt;
Then add the following overloading function at the end of your customfields_fields_extend.lib.php file (read the comments for more informations):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_propaldet_vl (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
&lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $vl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;vl&#039;};&lt;br /&gt;
    $cpl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;cpl&#039;};&lt;br /&gt;
    $lc = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;lc&#039;};&lt;br /&gt;
&lt;br /&gt;
    // Compute the discount&lt;br /&gt;
    $remise_calculee = (100 - $vl) * (100 - $cpl) * (100 - $lc);&lt;br /&gt;
&lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $remise_calculee = price2num($remise_calculee);&lt;br /&gt;
&lt;br /&gt;
    // Assign the new discount into the PropalLine object&lt;br /&gt;
    $object-&amp;gt;remise_percent = $remise_calculee;&lt;br /&gt;
&lt;br /&gt;
    // Update the price (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
&lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
&lt;br /&gt;
    //$q = &amp;quot;UPDATE  &amp;quot;.MAIN_DB_PREFIX.&amp;quot;propaldet SET remise_percent=&amp;quot;.$remise_calculee.&amp;quot; WHERE  rowid=&amp;quot;.$object-&amp;gt;rowid;&lt;br /&gt;
    //$db-&amp;gt;query($q);&lt;br /&gt;
    &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;vl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;cpl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;lc&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Linking Dolibarr objects from two different modules =&lt;br /&gt;
&lt;br /&gt;
Dolibarr sometimes offers the possibility to link two different modules. Eg: when you convert an Intervention card into an Invoice.&lt;br /&gt;
&lt;br /&gt;
In this case, you might want in your templates to fetch the custom fields of the linked object. Eg: you generate a PDF from the Invoice, but you want to get the custom fields from the Intervention that was converted into the Invoice.&lt;br /&gt;
&lt;br /&gt;
The key here is to use the &#039;&#039;&#039;llx_element_element&#039;&#039;&#039; table or the &#039;&#039;&#039;llx_element_*&#039;&#039;&#039; tables (eg: llx_element_contact): this is a standard Dolibarr table that stores all links between objects of two different modules.&lt;br /&gt;
&lt;br /&gt;
Here is the structure:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
rowid | fk_source | sourcetype | fk_target | targettype&lt;br /&gt;
-------------------------------------------------------&lt;br /&gt;
1 | 2 | fichinter | 5 | facture&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can then use a php code like the following to populate your $object with customfields:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
// This will also create and returns us an instance of the CustomFields class, which we will use to manually query the llx_element_element table&lt;br /&gt;
$customfields = customfields_fill_object($object, null, $outputlangs, &#039;facture&#039;, true); // beautified values&lt;br /&gt;
&lt;br /&gt;
// Querying the llx_element_element table manually to get the link between the invoice and the linked intervention card (we want the id of the intervention card)&lt;br /&gt;
// fetchAny($columns, $table, $where=&#039;&#039;, $orderby=&#039;&#039;, $limitby=&#039;&#039;)&lt;br /&gt;
$eltrow = $customfields-&amp;gt;fetchAny(&#039;*&#039;, MAIN_DB_PREFIX.&#039;element_element&#039;, &#039;targettype=&amp;quot;&#039;.$object-&amp;gt;element.&#039;&amp;quot; and fk_target=&#039;.$object-&amp;gt;id.&#039; and sourcetype=&amp;quot;fichinter&amp;quot;&#039;);&lt;br /&gt;
&lt;br /&gt;
// Save the intervention&#039;s id&lt;br /&gt;
$sourceid = $eltrow[0][&#039;fk_source&#039;];&lt;br /&gt;
&lt;br /&gt;
// Crafting a dummy intervention object&lt;br /&gt;
$fromobject = new stdClass(); // Init an empty object&lt;br /&gt;
$fromobject-&amp;gt;id = $sourceid; // We need the id and..&lt;br /&gt;
$fromobject-&amp;gt;table_element = &#039;fichinter&#039;; // the table_element (name of the table)&lt;br /&gt;
&lt;br /&gt;
// We can then fill $object with the intervention custom fields (we store them in a sub property &#039;fichinter&#039; to avoid conflicts with invoice&#039;s custom fields)&lt;br /&gt;
customfields_fill_object($object, $fromobject, $outputlangs, &#039;fichinter&#039;, true);&lt;br /&gt;
&lt;br /&gt;
// Now you can access invoices custom fields using:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;facture-&amp;gt;cf_myfield);&lt;br /&gt;
// And access intervention custom fields with:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;fichinter-&amp;gt;cf_myfield);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to netassopro for the tip.&lt;br /&gt;
&lt;br /&gt;
= Modify the total global price with a coefficient (without tampering the discount) =&lt;br /&gt;
&lt;br /&gt;
This is more troublesome than modifying the total price per product/service line, because the total global price of an object (let&#039;s say an invoice, but this also works for any other object) is always recomputed by summing the prices and quantity of each product/service line. This means that although there&#039;s an entry &amp;quot;total_ttc&amp;quot; in the database to store the invoice&#039;s total price, this price is in fact always recomputed, it&#039;s never used as-is. Thus, it&#039;s useless to change only the database record: we need to inject some code to change how the total price is computed.&lt;br /&gt;
&lt;br /&gt;
This could be done using an overloading function or a trigger, but unluckily, Dolibarr doesn&#039;t call a trigger nor a hook when recomputing the price. We thus need to edit core files to do what we want.&lt;br /&gt;
&lt;br /&gt;
However, the bright side is that we can cleverly edit just two functions from one core file: update_price() and getMarginInfos() from /htdocs/core/class/commonobject.class.php , and everything should run smoothly. Beware that if you update Dolibarr, you will need to redo these two modifications in the code.&lt;br /&gt;
&lt;br /&gt;
First, you need to create a new custom field called &amp;quot;global_coefficient&amp;quot; in Invoices (not Invoices Lines!). Then you can continue to the subchapters below.&lt;br /&gt;
&lt;br /&gt;
Also note that this global coefficient is also compatible with per line coefficients (as described in the next chapter), thus you can use a global coefficient and per line coefficients at the same time. You can also use multiple global coefficient if you wish.&lt;br /&gt;
&lt;br /&gt;
Another note: if you have a grid of discounts depending on quantity or price or whatever parameter, you can program a precomputation code using a Custom Ajax function (this way, your discounts will be automatically computed, but then you can still manually enter them for particular cases since the custom field is editable).&lt;br /&gt;
&lt;br /&gt;
== Implement the change of global price in most of Dolibarr (including remaining debt to pay and PDF and ODT) ==&lt;br /&gt;
&lt;br /&gt;
The change we will implement here will reflect the new total price with the global coefficient in most of Dolibarr&#039;s code, since we will here change the main function that is usually called to update and get the total price.&lt;br /&gt;
&lt;br /&gt;
Open /htdocs/core/class/commonobject.class.php and look for the function update_price().&lt;br /&gt;
&lt;br /&gt;
You should see the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function update_price($exclspec=0,$roundingadjust=&#039;none&#039;,$nodatabaseupdate=0,$seller=&#039;&#039;)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just below the include_once line, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;;&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/conf/conf_customfields_func.lib.php&#039;;&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $this-&amp;gt;table_element);&lt;br /&gt;
// Get object&#039;s id (either stored in property &amp;quot;id&amp;quot; or &amp;quot;rowid&amp;quot;)&lt;br /&gt;
$this_id = (!empty($this-&amp;gt;id)) ? $this-&amp;gt;id : $this-&amp;gt;rowid;&lt;br /&gt;
// Load custom fields records&lt;br /&gt;
$cf_parent_record = $customfields-&amp;gt;fetch($this_id);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will load the global_coefficient custom field (and also any other custom field you have defined for invoices).&lt;br /&gt;
&lt;br /&gt;
Now scroll below, and you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
while ($i &amp;lt; $num)&lt;br /&gt;
{&lt;br /&gt;
    $obj = $this-&amp;gt;db-&amp;gt;fetch_object($resql);&lt;br /&gt;
&lt;br /&gt;
    ... (lots of code)&lt;br /&gt;
&lt;br /&gt;
    i++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Add revenue stamp to total&lt;br /&gt;
$this-&amp;gt;total_ttc       += isset($this-&amp;gt;revenuestamp)?$this-&amp;gt;revenuestamp:0;&lt;br /&gt;
&lt;br /&gt;
$this-&amp;gt;db-&amp;gt;free($resql);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is the end of the loop that is summing each product/service line price to compute the total price. Just after this loop, you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Now update global field total_ht, total_ttc and tva&lt;br /&gt;
$fieldht=&#039;total_ht&#039;;&lt;br /&gt;
$fieldtva=&#039;tva&#039;;&lt;br /&gt;
$fieldlocaltax1=&#039;localtax1&#039;;&lt;br /&gt;
$fieldlocaltax2=&#039;localtax2&#039;;&lt;br /&gt;
$fieldttc=&#039;total_ttc&#039;;&lt;br /&gt;
// Specific code for backward compatibility with old field names&lt;br /&gt;
if ($this-&amp;gt;element == &#039;facture&#039; || $this-&amp;gt;element == &#039;facturerec&#039;)             $fieldht=&#039;total&#039;;&lt;br /&gt;
if ($this-&amp;gt;element == &#039;facture_fourn&#039; || $this-&amp;gt;element == &#039;invoice_supplier&#039;) $fieldtva=&#039;total_tva&#039;;&lt;br /&gt;
if ($this-&amp;gt;element == &#039;propal&#039;)                                                $fieldttc=&#039;total&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Between those two blocks of code (so just above // Now update global field total_ht, total_ttc and tva), you can place the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
if (!empty($cf_parent_record-&amp;gt;global_coefficient)) { // avoid applying the coefficient if empty (this will return a 0 price)&lt;br /&gt;
    $this-&amp;gt;total_ht = $this-&amp;gt;total_ht * $cf_parent_record-&amp;gt;global_coefficient;&lt;br /&gt;
    $this-&amp;gt;total_tva = $this-&amp;gt;total_tva * $cf_parent_record-&amp;gt;global_coefficient;&lt;br /&gt;
    $this-&amp;gt;total_ttc = $this-&amp;gt;total_ttc * $cf_parent_record-&amp;gt;global_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Done, now try to add/edit a product/service line, and the total price should be affected by the global coefficient.&lt;br /&gt;
&lt;br /&gt;
Note that this modification &#039;&#039;&#039;needs only to be done once for all modules&#039;&#039;&#039;, you then just have to create a custom field named &amp;quot;global_coefficient&amp;quot; in every module you want to apply this coefficient.&lt;br /&gt;
&lt;br /&gt;
== Implementing the change of price in margin module ==&lt;br /&gt;
&lt;br /&gt;
If you enabled the margin module, you should see that the coefficient is not applied there, but it&#039;s easy to fix that.&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/class/commonobject.class.php and find the function getMarginInfos(), then inside find the following lines:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach($this-&amp;gt;lines as $line) {&lt;br /&gt;
    if (empty($line-&amp;gt;pa_ht) &amp;amp;&amp;amp; isset($line-&amp;gt;fk_fournprice) &amp;amp;&amp;amp; !$force_price) {&lt;br /&gt;
        $product = new ProductFournisseur($this-&amp;gt;db);&lt;br /&gt;
        if ($product-&amp;gt;fetch_product_fournisseur_price($line-&amp;gt;fk_fournprice))&lt;br /&gt;
            $line-&amp;gt;pa_ht = $product-&amp;gt;fourn_unitprice * (1 - $product-&amp;gt;fourn_remise_percent / 100);&lt;br /&gt;
        if (isset($conf-&amp;gt;global-&amp;gt;MARGIN_TYPE) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;MARGIN_TYPE == &amp;quot;2&amp;quot; &amp;amp;&amp;amp; $product-&amp;gt;fourn_unitcharges &amp;gt; 0)&lt;br /&gt;
            $line-&amp;gt;pa_ht += $product-&amp;gt;fourn_unitcharges;&lt;br /&gt;
    }&lt;br /&gt;
    // si prix d&#039;achat non renseigné et devrait l&#039;être, alors prix achat = prix vente&lt;br /&gt;
    if ((!isset($line-&amp;gt;pa_ht) || $line-&amp;gt;pa_ht == 0) &amp;amp;&amp;amp; $line-&amp;gt;subprice &amp;gt; 0 &amp;amp;&amp;amp; (isset($conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull == 1)) {&lt;br /&gt;
        $line-&amp;gt;pa_ht = $line-&amp;gt;subprice * (1 - ($line-&amp;gt;remise_percent / 100));&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the lines are filled with margin infos. The price has just been fetched here, and we will now modify it with the coefficient before the price gets used for margin computation.&lt;br /&gt;
&lt;br /&gt;
To optimize things up, we will load the custom fields before the loop (because they don&#039;t change between product/service lines, they are the custom fields of the parent object), and then apply the global_coefficient inside the loop.&lt;br /&gt;
&lt;br /&gt;
Just above the code we found (just before the for loop), paste the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
// Get object&#039;s id (either stored in property &amp;quot;id&amp;quot; or &amp;quot;rowid&amp;quot;)&lt;br /&gt;
$this_id = (!empty($this-&amp;gt;id)) ? $this-&amp;gt;id : $this-&amp;gt;rowid;&lt;br /&gt;
// Load custom fields records&lt;br /&gt;
$customfields = customfields_fill_object($this);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And now, just below the code we found, inside the loop (and just above the comment: &amp;quot;// calcul des marges&amp;quot;), paste the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
// Apply the coefficient&lt;br /&gt;
if (!empty($this-&amp;gt;customfields-&amp;gt;cf_global_coefficient)) {&lt;br /&gt;
    $line-&amp;gt;pa_ht = $line-&amp;gt;pa_ht * $this-&amp;gt;customfields-&amp;gt;cf_global_coefficient;&lt;br /&gt;
    $line-&amp;gt;subprice = $line-&amp;gt;subprice * $this-&amp;gt;customfields-&amp;gt;cf_global_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that here we cheated a bit: we do not compute the coefficient on the global total price, but rather per product/service line price. This is done on purpose, because mathematically this should produce no difference, and it also allows Dolibarr to continue peacefully with its margin calculations (else we would have to adapt all those computations, which would imply a lot more of code changes!).&lt;br /&gt;
&lt;br /&gt;
That&#039;s all, the price should be reflected in the margin. Just refresh the page to reflect the change.&lt;br /&gt;
&lt;br /&gt;
Note that this modification &#039;&#039;&#039;needs only to be done once for all modules&#039;&#039;&#039;, you then just have to create a custom field named &amp;quot;global_coefficient&amp;quot; in every module you want to apply this coefficient.&lt;br /&gt;
&lt;br /&gt;
== Refresh the price instantly ==&lt;br /&gt;
&lt;br /&gt;
Maybe you noticed one glitch: when you modify the global_coefficient, the global total price isn&#039;t updated until you add/edit a product/service line. This is because the update_price() function is only called when we add/edit a product/service line.&lt;br /&gt;
&lt;br /&gt;
To fix that, we need now to force the refreshing of price. To do that, we will use an &amp;quot;aftersave&amp;quot; overloading function, which will execute commands after our custom field is saved.&lt;br /&gt;
&lt;br /&gt;
Go to the folder /htdocs/customfields/fields and rename the file customfields_fields_extend.lib.php.example into customfields_fields_extend.lib.php .&lt;br /&gt;
&lt;br /&gt;
Now, open it in your favourite text editor, and add the following function :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_aftersave_facture_global_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value, $fields) {&lt;br /&gt;
    // Force refreshing total price&lt;br /&gt;
    $object-&amp;gt;update_price(0, &#039;auto&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s all. Now, when you edit your global_coefficient, this will force refresh the total price.&lt;br /&gt;
&lt;br /&gt;
However, you will still notice one last glitch: when you edit the global_coefficient, the total price isn&#039;t reflected instantly, you must refresh the webpage once to see the new price. This is because of how the datasheet and the CustomFields hook are designed: CustomFields is called only exactly where the custom fields are place, thus it is called after the total price, the margin and the remaining debt are printed. Thus, CustomFields updates the value of your custom field only after all those informations get printed, and thus it cannot modify them at this stage. That&#039;s why those information are out-of-date, and you must refresh the webpage once to get to see the latest values.&lt;br /&gt;
&lt;br /&gt;
There is a workaround for this: refresh automatically the webpage (but delete the POST data to avoid an infinite loop). You just need to use this overloading code instead of the one above:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_aftersave_facture_global_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value, $fields) {&lt;br /&gt;
    // Force refreshing total price&lt;br /&gt;
    $object-&amp;gt;update_price(0, &#039;auto&#039;);&lt;br /&gt;
    // Force refresh the page to update prices that were printed before the custom field aftersave was triggered&lt;br /&gt;
    print(&#039;&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;window.location.href = window.location.pathname + window.location.search;&amp;lt;/script&amp;gt;&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Another way to fix this issue would be to modify how CustomFields save the custom fields, by moving this stage at the very top. However, this would need another hook, which should be called at the very beginning of the page loading, which should be made only for this purpose (PageLoading), and should be available in all modules supported by CustomFields in order to not break anything.&lt;br /&gt;
&lt;br /&gt;
Note that you will need to create one overloading function for each module you want to support with the global_coefficient custom field. For example, if you want to support both invoices and commercial proposals, you will need two overloading functions:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient for invoices (note the &amp;quot;facture&amp;quot; in the name of the function)&lt;br /&gt;
function customfields_field_aftersave_facture_global_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value, $fields) {&lt;br /&gt;
    // Force refreshing total price&lt;br /&gt;
    $object-&amp;gt;update_price(0, &#039;auto&#039;);&lt;br /&gt;
    // Force refresh the page to update prices that were printed before the custom field aftersave was triggered&lt;br /&gt;
    print(&#039;&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;window.location.href = window.location.pathname + window.location.search;&amp;lt;/script&amp;gt;&#039;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Global coefficient for commercial proposals (note the &amp;quot;propal&amp;quot; in the name of the function)&lt;br /&gt;
function customfields_field_aftersave_propal_global_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value, $fields) {&lt;br /&gt;
    // Force refreshing total price&lt;br /&gt;
    $object-&amp;gt;update_price(0, &#039;auto&#039;);&lt;br /&gt;
    // Force refresh the page to update prices that were printed before the custom field aftersave was triggered&lt;br /&gt;
    print(&#039;&amp;lt;script type=&amp;quot;text/javascript&amp;quot;&amp;gt;window.location.href = window.location.pathname + window.location.search;&amp;lt;/script&amp;gt;&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Modify the total price PER product/service line with a coefficient (without tampering the discount) =&lt;br /&gt;
&lt;br /&gt;
Here we will go through the creation and management of a custom field called &#039;&#039;&#039;coefficient&#039;&#039;&#039; that will change the total price of each product by modifying it by the coefficient.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will not create a coefficient per product but rather per product&#039;s line. Thus if you want to create a coefficient that never change between invoices but only depending on the product and stays static, please refer to the next chapter below about creating a custom tax per product.&lt;br /&gt;
&lt;br /&gt;
The preliminary and necessary step is to first create a custom field that will be used to modify the total price. We will create one for Client Invoice Lines.&lt;br /&gt;
&lt;br /&gt;
Go to the CustomFields administration panel, then into the Client Invoice Lines tab, then click on the button New Field, name the field &#039;&#039;&#039;coefficient&#039;&#039;&#039;, and set the type to &#039;&#039;&#039;Double&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
When you go to client invoices in Dolibarr, you should then see something similar to this:&lt;br /&gt;
[[File:Cfe1.jpg]]&lt;br /&gt;
You can see that our coefficient field was successfully created.&lt;br /&gt;
&lt;br /&gt;
Then here are two alternative ways of implementing the change of the total price of each product relativery to this coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change total price in database ==&lt;br /&gt;
&lt;br /&gt;
Here we will use a &amp;quot;save&amp;quot; overloading function to change the total price directly in the database, thus we won&#039;t have to modify anything else (eg: nor PDF templating nor ODT) since the total price will be already updated in the database.&lt;br /&gt;
&lt;br /&gt;
Warning: this method will work only with CustomFields =&amp;gt; 3.2.23 since the &amp;quot;save&amp;quot; overloading function did not work for products lines before.&lt;br /&gt;
&lt;br /&gt;
Just add the following overloading function in your customfields_fields_extend.lib.php file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_facturedet_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
 &lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $coefficient = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;coefficient&#039;}; // you could also use $coefficient = $value;&lt;br /&gt;
 &lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $coefficient = price2num($coefficient);&lt;br /&gt;
 &lt;br /&gt;
    // Get the original subprices (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
 &lt;br /&gt;
    // Update the subprices with the coefficient&lt;br /&gt;
    $object-&amp;gt;total_ht = $object-&amp;gt;total_ht * $coefficient; // compute no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva = $object-&amp;gt;total_tva * $coefficient; // compute tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc * $coefficient; // compute tax included price&lt;br /&gt;
 &lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(&#039;&#039;,true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
 &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;coefficient&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should show the correct total price on the Dolibarr&#039;s interface without modifying the unit subprice (P.U. Unit):&lt;br /&gt;
&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
You have nothing to edit in your PDF or ODT template, since the new subprice with applied coefficient will be automatically printed instead of the old one without coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change printed total price per product without tampering the subprice in the database ==&lt;br /&gt;
&lt;br /&gt;
Here is a way to change the printed total price (the one shown on screen and in PDF/ODT) but without changing the total price in the database.&lt;br /&gt;
&lt;br /&gt;
The following sub-chapters are all independent, thus you can only follow the ones that you require, eg: if you only need to change the price in your PDF documents, just create a custom field then jump to the sub-chapter to &#039;&#039;&#039;Implement the change of price in PDF&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in ODT templates ===&lt;br /&gt;
&lt;br /&gt;
There&#039;s no eval implementation currently inside ODT templates, but there is a kind of hook implementation, called [[Create_an_ODT_document_template#Other_personalized_tags|substitution functions]].&lt;br /&gt;
&lt;br /&gt;
You can either create your own module, then create your own substitution function, but for the sake of simplicity in this tutorial, we will directly use the CustomFields substitution function. However, please remember that you will have to redo your changes everytime you will update the CustomFields module, so if you don&#039;t want to do that it would be better if you create your own module and substitution function.&lt;br /&gt;
&lt;br /&gt;
Open the file &#039;&#039;&#039;htdocs/customfields/core/substitutions/functions_customfields.lib.php&#039;&#039;&#039; and go to the function &#039;&#039;&#039;customfields_completesubstitutionarray_lines&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
At the very end of this function, but inside the customfields check:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) {&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
But just before the ending bracket, you should add the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($object-&amp;gt;table_element_line === &#039;facturedet&#039;) { // First check that we are in the right module, we don&#039;t want to use coefficient for other modules if we didn&#039;t define the custom field there&lt;br /&gt;
    $substitutionarray[&#039;line_price_full&#039;] = $substitutionarray[&#039;line_price_ttc&#039;] * $substitutionarray[&#039;cf_coefficient&#039;]; // multiply the total price with our coefficient&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code will add a new tag &#039;&#039;&#039;{line_price_full}&#039;&#039;&#039; that you can then use inside your ODT templates.&lt;br /&gt;
&lt;br /&gt;
Just place this tag in a table in your ODT document, between the table tags ( [!-- BEGIN row.lines --] ... [!-- END row.lines --] ), and this will print your full price.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in PDF templates ===&lt;br /&gt;
&lt;br /&gt;
First, we will create our own PDF template by copying an existing template, and then we will edit it to change the price of items.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Creating your own PDF template by copying an existing template&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Go into the following folder: &#039;&#039;&#039;htdocs/core/modules/facture/doc/&#039;&#039;&#039;, then copy the file &#039;&#039;&#039;pdf_crabe.modules.php&#039;&#039;&#039; and name the new one &#039;&#039;&#039;pdf_crabecoeff.modules.php&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Then open it in your favourite editor, and change the following lines:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures // change pdf_crabe into pdf_crabecoeff&lt;br /&gt;
{&lt;br /&gt;
...&lt;br /&gt;
    function __construct($db)&lt;br /&gt;
    {&lt;br /&gt;
        ...&lt;br /&gt;
	$this-&amp;gt;name = &amp;quot;crabe&amp;quot;; // change &amp;quot;crabe&amp;quot; into &amp;quot;crabecoeff&amp;quot;&lt;br /&gt;
        ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you have your own PDF template!&lt;br /&gt;
&lt;br /&gt;
Now you should &#039;&#039;&#039;enable your PDF template&#039;&#039;&#039; before doing anything else, by going into the Modules Administration panel, and into the Invoice admin page, then click on the ON button at the right of the &#039;&#039;&#039;crabecoeff&#039;&#039;&#039; entry to enable the template.&lt;br /&gt;
&lt;br /&gt;
Now, let&#039;s proceed onto modifying the prices.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Changing the prices automatically in your PDF&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
You can now do pretty much anything you want in your PDF template, but I will give you a mean to automatically change the prices of every products with our coefficient, as well as the total price, without editing too much the template.&lt;br /&gt;
&lt;br /&gt;
First, try to find the following code of line (should be inside the write_file() method):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf=pdf_getInstance($this-&amp;gt;format);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, just copy/paste the following code &#039;&#039;&#039;below&#039;&#039;&#039; pdf_getInstance():&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$this-&amp;gt;customfields = customfields_fill_object($object, null, $outputlangs, null, true); // beautified values&lt;br /&gt;
$this-&amp;gt;customfields_raw = customfields_fill_object($object, null, $outputlangs, &#039;raw&#039;, null); // raw values&lt;br /&gt;
$this-&amp;gt;customfields_lines = customfields_fill_object_lines($object, null, $outputlangs, null, true); // product lines&#039; values&lt;br /&gt;
&lt;br /&gt;
// Resetting every $object price because we will compute them anew&lt;br /&gt;
$object-&amp;gt;total_ht = 0;&lt;br /&gt;
$object-&amp;gt;total_tva = 0;&lt;br /&gt;
$object-&amp;gt;total_ttc = 0;&lt;br /&gt;
// For each line of product/service, we will recompute all the prices&lt;br /&gt;
foreach($object-&amp;gt;lines as $line) {&lt;br /&gt;
    // Get the coefficient custom field&#039;s value for the current line&lt;br /&gt;
    $coeff = $object-&amp;gt;customfields-&amp;gt;lines-&amp;gt;{$line-&amp;gt;rowid}-&amp;gt;cf_coefficient;&lt;br /&gt;
    // Recompute the price of this product/service line&lt;br /&gt;
    $line-&amp;gt;total_ht = $line-&amp;gt;total_ht*$coeff; // compute no tax price&lt;br /&gt;
    $line-&amp;gt;total_tva = $line-&amp;gt;total_tva*$coeff; // compute tax amount&lt;br /&gt;
    $line-&amp;gt;total_ttc = $line-&amp;gt;total_ttc*$coeff; // compute tax included price&lt;br /&gt;
    // Do the sum of all new products/services prices and tva (will be incremented with every product/service line)&lt;br /&gt;
    $object-&amp;gt;total_ht += $line-&amp;gt;total_ht; // global no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva += $line-&amp;gt;total_tva; // global vat/tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc += $line-&amp;gt;total_ttc; // global  tax included price&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you should try to generate your PDF document, and you should get something like this:&lt;br /&gt;
[[File:Cfe3.jpg]]&lt;br /&gt;
&lt;br /&gt;
Note: printing the coefficient as a column in your PDF document is also possible but will not be covered here because PDF templates will vary for every persons depending on your needs.&lt;br /&gt;
For guidelines about how to do this, you can read the documentation on [[Create_a_PDF_document_template|how to create a PDF template]] and [[Module_CustomFields#Implementing_in_PDF_templates|how to use custom fields in PDF templates]].&lt;br /&gt;
&lt;br /&gt;
Hint: you should be looking inside the following conditional block, inside the write_file() method:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
{&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of total price in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
Unluckily there is not (yet) any hooking context to modify how lines of products/services are printed, thus the only thing you can do is directly edit the &#039;&#039;&#039;template file&#039;&#039;&#039; and manually put inside your code to load your custom field and print it the way you want using HTML formatting, for our example:&lt;br /&gt;
&lt;br /&gt;
* For Dolibarr 3.3 and above, edit the file &#039;&#039;&#039;htdocs/core/tpl/objectline_view.tpl.php&#039;&#039;&#039;&lt;br /&gt;
* For Dolibarr 3.1 and 3.2, edit the file &#039;&#039;&#039;htdocs/core/tpl/freeproductline_view.tpl.php&#039;&#039;&#039; AND &#039;&#039;&#039;predefinedproductline_view.tpl.php&#039;&#039;&#039; (apply the same changes to both files)&lt;br /&gt;
&lt;br /&gt;
In the template file you opened, find the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And replace it by:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $object-&amp;gt;table_element_line; // you can use $object-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
&lt;br /&gt;
// Modifying the HT price and printing it on the Dolibarr&#039;s datasheet interface&lt;br /&gt;
// Applying a multiplying/dividing coefficient to an HT price will give a mathematically correct TTC price since the taxes are also a multiplier&lt;br /&gt;
// However, if you want to apply other mathematical operations (like addition or substaction), you should use the total price with $line-&amp;gt;total_ttc&lt;br /&gt;
if (!empty($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
    echo price($line-&amp;gt;total_ht * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient);&lt;br /&gt;
} else {&lt;br /&gt;
    echo price($line-&amp;gt;total_ht);&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should produce something like this:&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Technical note: here we are using customfields_fill_object() instead of customfields_fill_object_lines() (the latter being specifically adapted to lines and thus should theoretically be used here) for performance reasons because the template file we are editing is called in a loop, and customfields_fill_object_lines() should be called outside a loop (because it already does the job that is done by the loop), so here we &#039;tricks&#039; the customfields_fill_object() function to avoid this loop by submitting &#039;facturedet&#039; as a module by itself (when in fact it&#039;s a product lines table, but it works the same way nevertheless). This is a good demonstration of the flexibility of the CustomFields functions and methods.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the coefficient column in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
It&#039;s also possible to show the coefficient in a new column on the Dolibarr interface.&lt;br /&gt;
&lt;br /&gt;
To do that, you need to do two things:&lt;br /&gt;
&lt;br /&gt;
1- Add the column title &amp;quot;Coeff.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
2- Add the generator that will input the coefficient value for each line.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Add the column title &amp;quot;Coeff.&amp;quot;&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
Open in your favourite editor the file /htdocs/core/class/commonobject.class.php and inside the function printObjectLines (note the plural s), find the following lines of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Total HT&lt;br /&gt;
print &#039;&amp;lt;td align=&amp;quot;right&amp;quot; width=&amp;quot;50&amp;quot;&amp;gt;&#039;.$langs-&amp;gt;trans(&#039;TotalHTShort&#039;).&#039;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is what prints the Total HT price column. We will place the Coeff column just before.&lt;br /&gt;
&lt;br /&gt;
So, just above this code, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
dol_include_once(&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element_line);&lt;br /&gt;
$coeff_field = $customfields-&amp;gt;fetchFieldStruct(&#039;coefficient&#039;);&lt;br /&gt;
if (!empty($coeff_field)) {&lt;br /&gt;
    print &#039;&amp;lt;td align=&amp;quot;right&amp;quot; width=&amp;quot;50&amp;quot;&amp;gt;&#039;.$langs-&amp;gt;trans(&#039;Coeff.&#039;).&#039;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will take care of showing the Coeff. column, but only for modules where you created such a custom field (because the file we modified is a common object for all modules in Dolibarr, not just invoices, thus this modification will gracefully skip this custom field if it does not exists for other modules).&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Add the generator&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/tpl/objectline_view.tpl.php and try to find the following block of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php if ($line-&amp;gt;special_code == 3)	{ ?&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo $langs-&amp;gt;trans(&#039;Option&#039;); ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } else { ?&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is what prints the total HT price value for each line.&lt;br /&gt;
&lt;br /&gt;
Just above of this block of code, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $object-&amp;gt;table_element_line; // you can use $this-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
if (isset($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
    &amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient; ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just like for the column title, this code will take care of generating the coefficient value only if the custom field is defined, because this file is also a generic template for all modules of Dolibarr.&lt;br /&gt;
&lt;br /&gt;
At this point, everything is done, you should see the following:&lt;br /&gt;
[[File:Cf_wiki_coeff_column.png]]&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in margin module ===&lt;br /&gt;
&lt;br /&gt;
If you enabled the margin module, you should see that the coefficient is not applied there, but it&#039;s easy to fix that.&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/class/commonobject.class.php and find the function getMarginInfos(), then inside find the following lines:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach($this-&amp;gt;lines as $line) {&lt;br /&gt;
    if (empty($line-&amp;gt;pa_ht) &amp;amp;&amp;amp; isset($line-&amp;gt;fk_fournprice) &amp;amp;&amp;amp; !$force_price) {&lt;br /&gt;
        $product = new ProductFournisseur($this-&amp;gt;db);&lt;br /&gt;
        if ($product-&amp;gt;fetch_product_fournisseur_price($line-&amp;gt;fk_fournprice))&lt;br /&gt;
            $line-&amp;gt;pa_ht = $product-&amp;gt;fourn_unitprice * (1 - $product-&amp;gt;fourn_remise_percent / 100);&lt;br /&gt;
        if (isset($conf-&amp;gt;global-&amp;gt;MARGIN_TYPE) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;MARGIN_TYPE == &amp;quot;2&amp;quot; &amp;amp;&amp;amp; $product-&amp;gt;fourn_unitcharges &amp;gt; 0)&lt;br /&gt;
            $line-&amp;gt;pa_ht += $product-&amp;gt;fourn_unitcharges;&lt;br /&gt;
    }&lt;br /&gt;
    // si prix d&#039;achat non renseigné et devrait l&#039;être, alors prix achat = prix vente&lt;br /&gt;
    if ((!isset($line-&amp;gt;pa_ht) || $line-&amp;gt;pa_ht == 0) &amp;amp;&amp;amp; $line-&amp;gt;subprice &amp;gt; 0 &amp;amp;&amp;amp; (isset($conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull == 1)) {&lt;br /&gt;
        $line-&amp;gt;pa_ht = $line-&amp;gt;subprice * (1 - ($line-&amp;gt;remise_percent / 100));&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the lines are filled with margin infos. The price has just been fetched here, and we will now modify it with the coefficient before the price gets used for margin computation.&lt;br /&gt;
&lt;br /&gt;
Just below the code we found, paste the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $line-&amp;gt;table_element;&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
if (!empty($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
    $line-&amp;gt;pa_ht = $line-&amp;gt;pa_ht * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient;&lt;br /&gt;
    $line-&amp;gt;subprice = $line-&amp;gt;subprice * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s all, the price should be reflected in the margin.&lt;br /&gt;
[[File:Cf_wiki_coeff_margin.png]]&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in remaining debt to pay ===&lt;br /&gt;
&lt;br /&gt;
Open /htdocs/core/class/commonobject.class.php and look for the function update_price().&lt;br /&gt;
&lt;br /&gt;
You should see the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function update_price($exclspec=0,$roundingadjust=&#039;none&#039;,$nodatabaseupdate=0,$seller=&#039;&#039;)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just below the include_once line, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;;&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/conf/conf_customfields_func.lib.php&#039;;&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $this-&amp;gt;table_element_line);&lt;br /&gt;
// Fetching line ids from parent object id&lt;br /&gt;
$line_ids = $customfields-&amp;gt;fetchAny(&#039;rowid&#039;, MAIN_DB_PREFIX.$this-&amp;gt;table_element_line, $this-&amp;gt;fk_element.&#039; = &#039;.$this-&amp;gt;id);&lt;br /&gt;
$line_ids = array_values_recursive(&#039;rowid&#039;, $line_ids);&lt;br /&gt;
// Load custom fields&lt;br /&gt;
$cf_records = $customfields-&amp;gt;fetch($line_ids);&lt;br /&gt;
// Reassociate the records with the line id (instead of the customfields record id)&lt;br /&gt;
$cf_line_cfrowid_to_rowid = array_reassociate(&#039;rowid&#039;, &#039;fk_&#039;.$this-&amp;gt;table_element_line, $cf_records);&lt;br /&gt;
$tmp = $cf_records;&lt;br /&gt;
$cf_records = array();&lt;br /&gt;
foreach ($tmp as $k=&amp;gt;$v) { $cf_records[$cf_line_cfrowid_to_rowid[$k]] = $tmp[$k]; }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will load the coefficient custom field (and also any other custom field you have defined for lines).&lt;br /&gt;
&lt;br /&gt;
Now scroll below, and you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
while ($i &amp;lt; $num)&lt;br /&gt;
{&lt;br /&gt;
    $obj = $this-&amp;gt;db-&amp;gt;fetch_object($resql);&lt;br /&gt;
&lt;br /&gt;
    // Note: There is no check on detail line and no check on total, if $forcedroundingmode = &#039;none&#039;&lt;br /&gt;
&lt;br /&gt;
    if ($forcedroundingmode == &#039;0&#039;)	// Check if data on line are consistent. This may solve lines that were not consistent because set with $forcedroundingmode=&#039;auto&#039;&lt;br /&gt;
    {&lt;br /&gt;
        $localtax_array=array($obj-&amp;gt;localtax1_type,$obj-&amp;gt;localtax1_tx,$obj-&amp;gt;localtax2_type,$obj-&amp;gt;localtax2_tx);&lt;br /&gt;
        $tmpcal=calcul_price_total($obj-&amp;gt;qty, $obj-&amp;gt;up, $obj-&amp;gt;remise_percent, $obj-&amp;gt;vatrate, $obj-&amp;gt;localtax1_tx, $obj-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $obj-&amp;gt;info_bits, $obj-&amp;gt;product_type, $seller, $localtax_array);&lt;br /&gt;
        $diff=price2num($tmpcal[1] - $obj-&amp;gt;total_tva, &#039;MT&#039;, 1);&lt;br /&gt;
        if ($diff)&lt;br /&gt;
        {&lt;br /&gt;
            $sqlfix=&amp;quot;UPDATE &amp;quot;.MAIN_DB_PREFIX.$this-&amp;gt;table_element_line.&amp;quot; SET &amp;quot;.$fieldtva.&amp;quot; = &amp;quot;.$tmpcal[1].&amp;quot;, total_ttc = &amp;quot;.$tmpcal[2].&amp;quot; WHERE rowid = &amp;quot;.$obj-&amp;gt;rowid;&lt;br /&gt;
            dol_syslog(&#039;We found unconsistent data into detailed line (difference of &#039;.$diff.&#039;) for line rowid = &#039;.$obj-&amp;gt;rowid.&amp;quot; (total vat of line calculated=&amp;quot;.$tmpcal[1].&amp;quot;, database=&amp;quot;.$obj-&amp;gt;total_tva.&amp;quot;). We fix the total_vat and total_ttc of line by running sqlfix = &amp;quot;.$sqlfix);&lt;br /&gt;
            $resqlfix=$this-&amp;gt;db-&amp;gt;query($sqlfix);&lt;br /&gt;
            if (! $resqlfix) dol_print_error($this-&amp;gt;db,&#039;Failed to update line&#039;);&lt;br /&gt;
            $obj-&amp;gt;total_tva = $tmpcal[1];&lt;br /&gt;
            $obj-&amp;gt;total_ttc = $tmpcal[2];&lt;br /&gt;
            //&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just after, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
$cf_coeff = $cf_records[$obj-&amp;gt;rowid]-&amp;gt;coefficient;&lt;br /&gt;
if (empty($cf_coeff) and $cf_coeff !== 0) $cf_coeff = 1; // by default, an empty coefficient = 1&lt;br /&gt;
$obj-&amp;gt;total_ht = $obj-&amp;gt;total_ht * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_tva = $obj-&amp;gt;total_tva * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_localtax1 = $obj-&amp;gt;total_localtax1 * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_localtax2 = $obj-&amp;gt;total_localtax2 * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_ttc = $obj-&amp;gt;total_ttc * $cf_coeff;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should do the trick. Now go to one of your invoices (or whatever module you are targetting), and try to add/edit a product/service line to force refresh the total price. You should then see this:&lt;br /&gt;
[[File:Cf_wiki_coeff_remaining_debt.png]]&lt;br /&gt;
&lt;br /&gt;
= Custom tax per product =&lt;br /&gt;
&lt;br /&gt;
This part will show you how to create a static custom tax per product. Static means that the tax will be stored per product, and not per product&#039;s line, thus you just have to setup the tax once per product and it will always be applied in every invoice you add this product.&lt;br /&gt;
&lt;br /&gt;
To do that, you will need the CustomFields Pro module + ProductsEasyAccess.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
1- Create a product&#039;s custom fields called &amp;quot;custom_tax&amp;quot;. Now, using ProductsEasyAccess, you can access this field wherever you wants, either to show it on the Dolibarr&#039;s interface (see previous chapter), or in a PDF template, which we will describe below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2- Modify your invoice&#039;s PDF template. You first need to load all the products&#039; fields &#039;&#039;before&#039;&#039; the loop over the products&#039; lines. To do this, search for a line like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// This should be placed above the &amp;quot;loop lines&amp;quot; below, add this:&lt;br /&gt;
&lt;br /&gt;
dol_include_once(&#039;/productseasyaccess/lib/productseasyaccess.lib.php&#039;);&lt;br /&gt;
fill_object_with_products_fields($object);&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines, search for this command&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  // Add this just below the for loop, this will ease your php manipulation after.&lt;br /&gt;
  // This will allow you to more easily access the products&#039; fields (this will store the id of each product inside $lineproductid).&lt;br /&gt;
  $line_product_id = $object-&amp;gt;lines[$i]-&amp;gt;fk_product;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4- You can now use your products&#039; fields in your PDF. To access the custom_tax field, just use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And to print it in your PDF:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell(0,3, $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax, 0, &#039;L&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5- Then, if you want to count the custom tax total and add it to your vat total, you can create a counter inside the for loop we cited in the first step:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_custom_tax = 0; // Initialize the custom tax total counter&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  $object-&amp;gt;total_custom_tax += $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then find inside the function **_tableau_tot**, find the following line:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And prepend the total modification like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc + $object-&amp;gt;total_custom_tax; // prepend this to add the custom tax to the total&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Done. This should show your custom tax per product and your total including all custom taxes.&lt;/div&gt;</summary>
		<author><name>Lrq3000</name></author>
	</entry>
	<entry>
		<id>https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33078</id>
		<title>Module CustomFields Cases</title>
		<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33078"/>
		<updated>2015-01-16T13:37:28Z</updated>

		<summary type="html">&lt;p&gt;Lrq3000: /* Implementing the change of price in margin module */ Add a note&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:CustomFields]]&lt;br /&gt;
{{TemplateDocDevEn}}&lt;br /&gt;
{{TemplateModEN}}&lt;br /&gt;
{{BasculeDevUserEn|&lt;br /&gt;
name=CustomFields|&lt;br /&gt;
num=8500|&lt;br /&gt;
devdoc=[[Module_CustomFields_Dev]]|&lt;br /&gt;
userdoc=[[Module_CustomFields]]|}}&lt;br /&gt;
This page lists a compilation of concrete, practical examples uses of [[Module_CustomFields|CustomFields]] and of its various features.&lt;br /&gt;
&lt;br /&gt;
If you had an experience with CustomFields, you can also add here your case as an example for others to get inspired.&lt;br /&gt;
&lt;br /&gt;
= How to make a generic PDF template listing all custom fields =&lt;br /&gt;
&lt;br /&gt;
To make a generic PDF template listing all custom fields, you just need to take an existing PDF template, then you need to follow these 3 steps:&lt;br /&gt;
&lt;br /&gt;
1/ Edit the class name (required, else it will conflict with the original template).&lt;br /&gt;
&lt;br /&gt;
Eg: if we take the crabe template for invoices, we change:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;crabe&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
into:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_customfields extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;customfields&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2/ Copy this generic function at the end of the PDF:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propale/order/etc... (CustomFields simpler functions will automatically adapt)&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs); // set default PDF font size&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
    // Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
    $customfields = customfields_fill_object($object, null, $outputlangs, null, true);&lt;br /&gt;
&lt;br /&gt;
    // Setting the starting position of the text cursor&lt;br /&gt;
    $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
    $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
    // Printing the customfields&lt;br /&gt;
    foreach ($object-&amp;gt;customfields as $label=&amp;gt;$value) { // $value is already formatted!&lt;br /&gt;
        // Get translated label&lt;br /&gt;
        $translatedlabel = $customfields-&amp;gt;findLabelPDF($label, $outputlangs); // translated label of the customfield (not translated by default in customfields_fill_object() because a field should always be accessible by a base name, whatever the translation is)&lt;br /&gt;
&lt;br /&gt;
        // PDF formatting, placement and printing&lt;br /&gt;
        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
        $pdf-&amp;gt;MultiCell(0,3, $translatedlabel.&#039; ($object-&amp;gt;customfields-&amp;gt;&#039;.$label.&#039;): &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternative function (with a bit less functionalities, but more controllable):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page (used to debug if CustomFields setup is correct)&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propal/product/whatever...&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs);&lt;br /&gt;
&lt;br /&gt;
            if (empty($object-&amp;gt;table_element) or empty($object-&amp;gt;id)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;Current \$object is not compatible with CustomFields, could not find table_element or id.&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
    $customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element);&lt;br /&gt;
&lt;br /&gt;
            // Fetching custom fields records&lt;br /&gt;
            $fields = $customfields-&amp;gt;fetch($object-&amp;gt;id);&lt;br /&gt;
&lt;br /&gt;
            if (!isset($fields)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;No custom field could be found for this object. Please check your configuration (did you create at least one customfield and set a value in the current datasheet?)&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            } else {&lt;br /&gt;
                // Setting the starting position of the text cursor&lt;br /&gt;
                $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
                $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
                // Printing the customfields&lt;br /&gt;
                foreach ($fields as $key=&amp;gt;$field) {&lt;br /&gt;
                        $translatedname = $customfields-&amp;gt;findLabelPDF($key, $outputlangs); // label of the customfield&lt;br /&gt;
                        $value = $customfields-&amp;gt;simpleprintFieldPDF($key, $field, $outputlangs); // value (cleaned and properly formatted) of the customfield&lt;br /&gt;
&lt;br /&gt;
                        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
                        $pdf-&amp;gt;MultiCell(0,3, $translatedname.&#039;: &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
                        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3/ Call the _pagecustomfields function at the right place (generally, after _pagefoot and just before $pdf-&amp;gt;Close()):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Pied de page&lt;br /&gt;
$this-&amp;gt;_pagefoot($pdf,$object,$outputlangs);&lt;br /&gt;
$pdf-&amp;gt;AliasNbPages();&lt;br /&gt;
&lt;br /&gt;
// CustomFields&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) { // if the customfields module is activated...&lt;br /&gt;
        // Add a page with the customfields&lt;br /&gt;
        $pdf-&amp;gt;AddPage();&lt;br /&gt;
        $this-&amp;gt;_pagecustomfields($pdf,$object,$outputlangs);&lt;br /&gt;
        $pagenb++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
$pdf-&amp;gt;Close(); // you should first search for this, you should only find one occurrence&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then go to the module&#039;s admin panel (eg: invoice admin panel) and enable the template you&#039;ve just created.&lt;br /&gt;
&lt;br /&gt;
You can now use it to test if your customfields work.&lt;br /&gt;
&lt;br /&gt;
= Conditions on fields selection =&lt;br /&gt;
&lt;br /&gt;
Sometimes you may want to show a DropdownBox or Constraint (or another multi-choice type), but you may want to show or hide choices based on conditions (eg: only if it&#039;s a prospect, only if this zone was selected, only if this category was selected, etc..).&lt;br /&gt;
&lt;br /&gt;
To know which is the best solution you should choose to implement your condition, you should first know in which category your condition falls in.&lt;br /&gt;
&lt;br /&gt;
== Conditions categories ==&lt;br /&gt;
&lt;br /&gt;
Conditions can be categorized into 4 types:&lt;br /&gt;
&lt;br /&gt;
* static condition: choices are restrained on a condition that never changes (eg: only show third-parties that are suppliers, or lowercase all data on sql insertion, or check that a field is above a certain number, etc.).&lt;br /&gt;
* semi-dynamic condition: choices that are constrained on a static table but based on the value of another field/customfield. This is also commonly called &amp;quot;Cascaded dropdown lists&amp;quot; or &amp;quot;Dynamically linked dropdown lists&amp;quot;.&lt;br /&gt;
* dynamic: everything is dynamic: choices are based on a subfield of a field of the current module, so that there&#039;s no material table and it&#039;s impossible to create a view (because you would need to create one view per id) (eg: show only the contacts attached to the third-party being accessed)&lt;br /&gt;
* timed condition: a condition that is based on time or recurrence (eg: at 5:00 AM delete old records, etc..)&lt;br /&gt;
&lt;br /&gt;
== Conditions solutions ==&lt;br /&gt;
&lt;br /&gt;
Now that you know what kind of condition you want to set, you can choose one of the following solutions depending on your preferences and knowledge.&lt;br /&gt;
&lt;br /&gt;
In any case, you can always use the &#039;&#039;&#039;CustomFields&#039;s overload functions&#039;&#039;&#039; to fit your needs for nearly every type of condition.&lt;br /&gt;
&lt;br /&gt;
=== Static condition ===&lt;br /&gt;
&lt;br /&gt;
* View: create a view on the table based on the condition, and then create a custom field constrained on this view (which is just like any table), eg: only show third-parties that are suppliers:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
CREATE VIEW llx_societe_supplier_view AS&lt;br /&gt;
SELECT *&lt;br /&gt;
FROM llx_societe&lt;br /&gt;
WHERE fournisseur=1;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
WARNING: foreign keys (and thus constraints) can&#039;t be used on views with MySQL, and materialized views neither exist, so you can&#039;t use this method with MySQL.&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
* SQL Transformations&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
=== Semi-dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade option is ideal in most cases, and if not enough, you can use the Cascade Custom option and Overloading Functions.&lt;br /&gt;
* View&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
&lt;br /&gt;
=== Dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade Custom and Overload functions&lt;br /&gt;
* Making your own module, which calls the CustomFields class&lt;br /&gt;
&lt;br /&gt;
=== Timed condition ===&lt;br /&gt;
&lt;br /&gt;
* SQL scheduled events&lt;br /&gt;
* Cron job&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
= Overloading functions =&lt;br /&gt;
&lt;br /&gt;
Here are a few concrete use cases of overloading functions to achieve various goals.&lt;br /&gt;
&lt;br /&gt;
== Dynamic list selection based on another one ==&lt;br /&gt;
&lt;br /&gt;
The goal is to show two custom fields on the Third-Party module: one which gives the zone (secteur) of the third-party (Isere, Alpes du sud, Haute-Savoie...) and the other which gives relative to the zone the list of all the ski resorts (station_a) inside the selected zone.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will show how to manually manage this kind of requirements using [[Module_CustomFields#Overloading_functions:_Adding_your_own_management_code_for_custom_fields|overloading functions]], but in latest CustomFields releases, the same solution can be achieved automatically using the [[Module_CustomFields#Cascade|Cascade option]].&lt;br /&gt;
&lt;br /&gt;
We have one table &#039;&#039;&#039;llx_k_station&#039;&#039;&#039; (rowid, station (char50), secteur(char50)) which contains the following: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1,les 2 alpes,isère&lt;br /&gt;
2,chamrousse,isère&lt;br /&gt;
3,alpe d&#039;huez,isère&lt;br /&gt;
4,vars,alpes du sud&lt;br /&gt;
5,risoul,alpes du sud&lt;br /&gt;
etc...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In CustomFields&#039;s admin panel, we create two custom fields:&lt;br /&gt;
&lt;br /&gt;
1- secteur: returns the list of all zones - type DropdownBox: enum(&#039;- Aucun&#039;,&#039;Alpes du Sud&#039;,&#039;export&#039;,&#039;Haute-Savoie&#039;,&#039;Isère&#039;, etc...)&lt;br /&gt;
&lt;br /&gt;
2- station_a: returns the list of all ski resorts - type constraint on &#039;&#039;&#039;llx_k_station&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Here is the code to put in customfields_fields_extend.lib.php that will allow to show only the ski resorts corresponding to the selected zone:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_editview_societe_station_a (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$parameters, &amp;amp;$action, &amp;amp;$user, &amp;amp;$idvar, &amp;amp;$rightok, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
  global $db; // allow to access database functions&lt;br /&gt;
  $sql=&amp;quot;SELECT llx_k_station.station, llx_k_station.rowid FROM llx_societe_customfields INNER JOIN llx_k_station ON llx_societe_customfields.Secteur = llx_k_station.secteur WHERE llx_societe_customfields.fk_societe=&amp;quot;.$object-&amp;gt;id;&lt;br /&gt;
$result=$db-&amp;gt;query($sql);&lt;br /&gt;
 &lt;br /&gt;
  if ($result) {&lt;br /&gt;
    $num = $db-&amp;gt;num_rows($result);&lt;br /&gt;
    $i = 0;&lt;br /&gt;
    $myarray = array();&lt;br /&gt;
    while ($i &amp;lt; $num)&lt;br /&gt;
    {&lt;br /&gt;
      $obj = $db-&amp;gt;fetch_object($result);&lt;br /&gt;
      $myarray []=array(&#039;id&#039;=&amp;gt;$obj-&amp;gt;rowid, &#039;value&#039;=&amp;gt;$obj-&amp;gt;station);&lt;br /&gt;
      $i++;&lt;br /&gt;
    }&lt;br /&gt;
    $value = $myarray; &lt;br /&gt;
    $db-&amp;gt;free($result);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to manub for giving this case.&lt;br /&gt;
&lt;br /&gt;
== Dynamic discount computation per products lines on propales ==&lt;br /&gt;
&lt;br /&gt;
The goal here is to compute the discount for a product line in propales based on a computation on three custom fields.&lt;br /&gt;
&lt;br /&gt;
First, create three custom fields for &amp;quot;Line commercial proposals&amp;quot;: vl, cpl and lc.&lt;br /&gt;
&lt;br /&gt;
Then add the following overloading function at the end of your customfields_fields_extend.lib.php file (read the comments for more informations):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_propaldet_vl (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
&lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $vl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;vl&#039;};&lt;br /&gt;
    $cpl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;cpl&#039;};&lt;br /&gt;
    $lc = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;lc&#039;};&lt;br /&gt;
&lt;br /&gt;
    // Compute the discount&lt;br /&gt;
    $remise_calculee = (100 - $vl) * (100 - $cpl) * (100 - $lc);&lt;br /&gt;
&lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $remise_calculee = price2num($remise_calculee);&lt;br /&gt;
&lt;br /&gt;
    // Assign the new discount into the PropalLine object&lt;br /&gt;
    $object-&amp;gt;remise_percent = $remise_calculee;&lt;br /&gt;
&lt;br /&gt;
    // Update the price (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
&lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
&lt;br /&gt;
    //$q = &amp;quot;UPDATE  &amp;quot;.MAIN_DB_PREFIX.&amp;quot;propaldet SET remise_percent=&amp;quot;.$remise_calculee.&amp;quot; WHERE  rowid=&amp;quot;.$object-&amp;gt;rowid;&lt;br /&gt;
    //$db-&amp;gt;query($q);&lt;br /&gt;
    &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;vl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;cpl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;lc&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Linking Dolibarr objects from two different modules =&lt;br /&gt;
&lt;br /&gt;
Dolibarr sometimes offers the possibility to link two different modules. Eg: when you convert an Intervention card into an Invoice.&lt;br /&gt;
&lt;br /&gt;
In this case, you might want in your templates to fetch the custom fields of the linked object. Eg: you generate a PDF from the Invoice, but you want to get the custom fields from the Intervention that was converted into the Invoice.&lt;br /&gt;
&lt;br /&gt;
The key here is to use the &#039;&#039;&#039;llx_element_element&#039;&#039;&#039; table or the &#039;&#039;&#039;llx_element_*&#039;&#039;&#039; tables (eg: llx_element_contact): this is a standard Dolibarr table that stores all links between objects of two different modules.&lt;br /&gt;
&lt;br /&gt;
Here is the structure:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
rowid | fk_source | sourcetype | fk_target | targettype&lt;br /&gt;
-------------------------------------------------------&lt;br /&gt;
1 | 2 | fichinter | 5 | facture&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can then use a php code like the following to populate your $object with customfields:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
// This will also create and returns us an instance of the CustomFields class, which we will use to manually query the llx_element_element table&lt;br /&gt;
$customfields = customfields_fill_object($object, null, $outputlangs, &#039;facture&#039;, true); // beautified values&lt;br /&gt;
&lt;br /&gt;
// Querying the llx_element_element table manually to get the link between the invoice and the linked intervention card (we want the id of the intervention card)&lt;br /&gt;
// fetchAny($columns, $table, $where=&#039;&#039;, $orderby=&#039;&#039;, $limitby=&#039;&#039;)&lt;br /&gt;
$eltrow = $customfields-&amp;gt;fetchAny(&#039;*&#039;, MAIN_DB_PREFIX.&#039;element_element&#039;, &#039;targettype=&amp;quot;&#039;.$object-&amp;gt;element.&#039;&amp;quot; and fk_target=&#039;.$object-&amp;gt;id.&#039; and sourcetype=&amp;quot;fichinter&amp;quot;&#039;);&lt;br /&gt;
&lt;br /&gt;
// Save the intervention&#039;s id&lt;br /&gt;
$sourceid = $eltrow[0][&#039;fk_source&#039;];&lt;br /&gt;
&lt;br /&gt;
// Crafting a dummy intervention object&lt;br /&gt;
$fromobject = new stdClass(); // Init an empty object&lt;br /&gt;
$fromobject-&amp;gt;id = $sourceid; // We need the id and..&lt;br /&gt;
$fromobject-&amp;gt;table_element = &#039;fichinter&#039;; // the table_element (name of the table)&lt;br /&gt;
&lt;br /&gt;
// We can then fill $object with the intervention custom fields (we store them in a sub property &#039;fichinter&#039; to avoid conflicts with invoice&#039;s custom fields)&lt;br /&gt;
customfields_fill_object($object, $fromobject, $outputlangs, &#039;fichinter&#039;, true);&lt;br /&gt;
&lt;br /&gt;
// Now you can access invoices custom fields using:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;facture-&amp;gt;cf_myfield);&lt;br /&gt;
// And access intervention custom fields with:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;fichinter-&amp;gt;cf_myfield);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to netassopro for the tip.&lt;br /&gt;
&lt;br /&gt;
= Modify the total global price with a coefficient (without tampering the discount) =&lt;br /&gt;
&lt;br /&gt;
This is more troublesome than modifying the total price per product/service line, because the total global price of an object (let&#039;s say an invoice, but this also works for any other object) is always recomputed by summing the prices and quantity of each product/service line. This means that although there&#039;s an entry &amp;quot;total_ttc&amp;quot; in the database to store the invoice&#039;s total price, this price is in fact always recomputed, it&#039;s never used as-is. Thus, it&#039;s useless to change only the database record: we need to inject some code to change how the total price is computed.&lt;br /&gt;
&lt;br /&gt;
This could be done using an overloading function or a trigger, but unluckily, Dolibarr doesn&#039;t call a trigger nor a hook when recomputing the price. We thus need to edit core files to do what we want.&lt;br /&gt;
&lt;br /&gt;
However, the bright side is that we can cleverly edit just two functions from one core file: update_price() and getMarginInfos() from /htdocs/core/class/commonobject.class.php , and everything should run smoothly. Beware that if you update Dolibarr, you will need to redo these two modifications in the code.&lt;br /&gt;
&lt;br /&gt;
First, you need to create a new custom field called &amp;quot;global_coefficient&amp;quot; in Invoices (not Invoices Lines!). Then you can continue to the subchapters below.&lt;br /&gt;
&lt;br /&gt;
Also note that this global coefficient is also compatible with per line coefficients (as described in the next chapter), thus you can use a global coefficient and per line coefficients at the same time. You can also use multiple global coefficient if you wish.&lt;br /&gt;
&lt;br /&gt;
Another note: if you have a grid of discounts depending on quantity or price or whatever parameter, you can program a precomputation code using a Custom Ajax function (this way, your discounts will be automatically computed, but then you can still manually enter them for particular cases since the custom field is editable).&lt;br /&gt;
&lt;br /&gt;
== Implement the change of global price in most of Dolibarr (including remaining debt to pay and PDF and ODT) ==&lt;br /&gt;
&lt;br /&gt;
The change we will implement here will reflect the new total price with the global coefficient in most of Dolibarr&#039;s code, since we will here change the main function that is usually called to update and get the total price.&lt;br /&gt;
&lt;br /&gt;
Open /htdocs/core/class/commonobject.class.php and look for the function update_price().&lt;br /&gt;
&lt;br /&gt;
You should see the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function update_price($exclspec=0,$roundingadjust=&#039;none&#039;,$nodatabaseupdate=0,$seller=&#039;&#039;)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just below the include_once line, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;;&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/conf/conf_customfields_func.lib.php&#039;;&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $this-&amp;gt;table_element);&lt;br /&gt;
// Get object&#039;s id (either stored in property &amp;quot;id&amp;quot; or &amp;quot;rowid&amp;quot;)&lt;br /&gt;
$this_id = (!empty($this-&amp;gt;id)) ? $this-&amp;gt;id : $this-&amp;gt;rowid;&lt;br /&gt;
// Load custom fields records&lt;br /&gt;
$cf_parent_record = $customfields-&amp;gt;fetch($this_id);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will load the global_coefficient custom field (and also any other custom field you have defined for invoices).&lt;br /&gt;
&lt;br /&gt;
Now scroll below, and you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
while ($i &amp;lt; $num)&lt;br /&gt;
{&lt;br /&gt;
    $obj = $this-&amp;gt;db-&amp;gt;fetch_object($resql);&lt;br /&gt;
&lt;br /&gt;
    ... (lots of code)&lt;br /&gt;
&lt;br /&gt;
    i++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Add revenue stamp to total&lt;br /&gt;
$this-&amp;gt;total_ttc       += isset($this-&amp;gt;revenuestamp)?$this-&amp;gt;revenuestamp:0;&lt;br /&gt;
&lt;br /&gt;
$this-&amp;gt;db-&amp;gt;free($resql);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is the end of the loop that is summing each product/service line price to compute the total price. Just after this loop, you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Now update global field total_ht, total_ttc and tva&lt;br /&gt;
$fieldht=&#039;total_ht&#039;;&lt;br /&gt;
$fieldtva=&#039;tva&#039;;&lt;br /&gt;
$fieldlocaltax1=&#039;localtax1&#039;;&lt;br /&gt;
$fieldlocaltax2=&#039;localtax2&#039;;&lt;br /&gt;
$fieldttc=&#039;total_ttc&#039;;&lt;br /&gt;
// Specific code for backward compatibility with old field names&lt;br /&gt;
if ($this-&amp;gt;element == &#039;facture&#039; || $this-&amp;gt;element == &#039;facturerec&#039;)             $fieldht=&#039;total&#039;;&lt;br /&gt;
if ($this-&amp;gt;element == &#039;facture_fourn&#039; || $this-&amp;gt;element == &#039;invoice_supplier&#039;) $fieldtva=&#039;total_tva&#039;;&lt;br /&gt;
if ($this-&amp;gt;element == &#039;propal&#039;)                                                $fieldttc=&#039;total&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Between those two blocks of code (so just above // Now update global field total_ht, total_ttc and tva), you can place the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
if (!empty($cf_parent_record-&amp;gt;global_coefficient)) { // avoid applying the coefficient if empty (this will return a 0 price)&lt;br /&gt;
    $this-&amp;gt;total_ht = $this-&amp;gt;total_ht * $cf_parent_record-&amp;gt;global_coefficient;&lt;br /&gt;
    $this-&amp;gt;total_tva = $this-&amp;gt;total_tva * $cf_parent_record-&amp;gt;global_coefficient;&lt;br /&gt;
    $this-&amp;gt;total_ttc = $this-&amp;gt;total_ttc * $cf_parent_record-&amp;gt;global_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Done, now try to add/edit a product/service line, and the total price should be affected by the global coefficient.&lt;br /&gt;
&lt;br /&gt;
Note that this modification &#039;&#039;&#039;needs only to be done once for all modules&#039;&#039;&#039;, you then just have to create a custom field named &amp;quot;global_coefficient&amp;quot; in every module you want to apply this coefficient.&lt;br /&gt;
&lt;br /&gt;
== Implementing the change of price in margin module ==&lt;br /&gt;
&lt;br /&gt;
If you enabled the margin module, you should see that the coefficient is not applied there, but it&#039;s easy to fix that.&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/class/commonobject.class.php and find the function getMarginInfos(), then inside find the following lines:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach($this-&amp;gt;lines as $line) {&lt;br /&gt;
    if (empty($line-&amp;gt;pa_ht) &amp;amp;&amp;amp; isset($line-&amp;gt;fk_fournprice) &amp;amp;&amp;amp; !$force_price) {&lt;br /&gt;
        $product = new ProductFournisseur($this-&amp;gt;db);&lt;br /&gt;
        if ($product-&amp;gt;fetch_product_fournisseur_price($line-&amp;gt;fk_fournprice))&lt;br /&gt;
            $line-&amp;gt;pa_ht = $product-&amp;gt;fourn_unitprice * (1 - $product-&amp;gt;fourn_remise_percent / 100);&lt;br /&gt;
        if (isset($conf-&amp;gt;global-&amp;gt;MARGIN_TYPE) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;MARGIN_TYPE == &amp;quot;2&amp;quot; &amp;amp;&amp;amp; $product-&amp;gt;fourn_unitcharges &amp;gt; 0)&lt;br /&gt;
            $line-&amp;gt;pa_ht += $product-&amp;gt;fourn_unitcharges;&lt;br /&gt;
    }&lt;br /&gt;
    // si prix d&#039;achat non renseigné et devrait l&#039;être, alors prix achat = prix vente&lt;br /&gt;
    if ((!isset($line-&amp;gt;pa_ht) || $line-&amp;gt;pa_ht == 0) &amp;amp;&amp;amp; $line-&amp;gt;subprice &amp;gt; 0 &amp;amp;&amp;amp; (isset($conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull == 1)) {&lt;br /&gt;
        $line-&amp;gt;pa_ht = $line-&amp;gt;subprice * (1 - ($line-&amp;gt;remise_percent / 100));&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the lines are filled with margin infos. The price has just been fetched here, and we will now modify it with the coefficient before the price gets used for margin computation.&lt;br /&gt;
&lt;br /&gt;
To optimize things up, we will load the custom fields before the loop (because they don&#039;t change between product/service lines, they are the custom fields of the parent object), and then apply the global_coefficient inside the loop.&lt;br /&gt;
&lt;br /&gt;
Just above the code we found (just before the for loop), paste the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
// Get object&#039;s id (either stored in property &amp;quot;id&amp;quot; or &amp;quot;rowid&amp;quot;)&lt;br /&gt;
$this_id = (!empty($this-&amp;gt;id)) ? $this-&amp;gt;id : $this-&amp;gt;rowid;&lt;br /&gt;
// Load custom fields records&lt;br /&gt;
$customfields = customfields_fill_object($this);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And now, just below the code we found, inside the loop (and just above the comment: &amp;quot;// calcul des marges&amp;quot;), paste the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
// Apply the coefficient&lt;br /&gt;
if (!empty($this-&amp;gt;customfields-&amp;gt;cf_global_coefficient)) {&lt;br /&gt;
    $line-&amp;gt;pa_ht = $line-&amp;gt;pa_ht * $this-&amp;gt;customfields-&amp;gt;cf_global_coefficient;&lt;br /&gt;
    $line-&amp;gt;subprice = $line-&amp;gt;subprice * $this-&amp;gt;customfields-&amp;gt;cf_global_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that here we cheated a bit: we do not compute the coefficient on the global total price, but rather per product/service line price. This is done on purpose, because mathematically this should produce no difference, and it also allows Dolibarr to continue peacefully with its margin calculations (else we would have to adapt all those computations, which would imply a lot more of code changes!).&lt;br /&gt;
&lt;br /&gt;
That&#039;s all, the price should be reflected in the margin. Just refresh the page to reflect the change.&lt;br /&gt;
&lt;br /&gt;
Note that this modification &#039;&#039;&#039;needs only to be done once for all modules&#039;&#039;&#039;, you then just have to create a custom field named &amp;quot;global_coefficient&amp;quot; in every module you want to apply this coefficient.&lt;br /&gt;
&lt;br /&gt;
== Refresh the price instantly ==&lt;br /&gt;
&lt;br /&gt;
Maybe you noticed one glitch: when you modify the global_coefficient, the global total price isn&#039;t updated until you add/edit a product/service line. This is because the update_price() function is only called when we add/edit a product/service line.&lt;br /&gt;
&lt;br /&gt;
To fix that, we need now to force the refreshing of price. To do that, we will use an &amp;quot;aftersave&amp;quot; overloading function, which will execute commands after our custom field is saved.&lt;br /&gt;
&lt;br /&gt;
Go to the folder /htdocs/customfields/fields and rename the file customfields_fields_extend.lib.php.example into customfields_fields_extend.lib.php .&lt;br /&gt;
&lt;br /&gt;
Now, open it in your favourite text editor, and add the following function :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_aftersave_facture_global_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value, $fields) {&lt;br /&gt;
    // Force refreshing total price&lt;br /&gt;
    $object-&amp;gt;parent_object-&amp;gt;update_price(0, &#039;auto&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s all. Now, when you edit your global_coefficient, this will force refresh the total price.&lt;br /&gt;
&lt;br /&gt;
However, you will still notice one last glitch: when you edit the global_coefficient, the total price isn&#039;t reflected instantly, you must refresh the webpage once to see the new price. This is because of how the datasheet and the CustomFields hook are designed: CustomFields is called only exactly where the custom fields are place, thus it is called after the total price, the margin and the remaining debt are printed. Thus, CustomFields updates the value of your custom field only after all those informations get printed, and thus it cannot modify them at this stage. That&#039;s why those information are out-of-date, and you must refresh the webpage once to get to see the latest values.&lt;br /&gt;
&lt;br /&gt;
There is nothing you can do about it. One way to fix that would be to modify how CustomFields save the custom fields, by moving this stage at the very top. However, this would need another hook, which should be called at the very beginning of the page loading, which should be made only for this purpose (PageLoading), and should be available in all modules supported by CustomFields in order to not break anything.&lt;br /&gt;
&lt;br /&gt;
= Modify the total price PER product/service line with a coefficient (without tampering the discount) =&lt;br /&gt;
&lt;br /&gt;
Here we will go through the creation and management of a custom field called &#039;&#039;&#039;coefficient&#039;&#039;&#039; that will change the total price of each product by modifying it by the coefficient.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will not create a coefficient per product but rather per product&#039;s line. Thus if you want to create a coefficient that never change between invoices but only depending on the product and stays static, please refer to the next chapter below about creating a custom tax per product.&lt;br /&gt;
&lt;br /&gt;
The preliminary and necessary step is to first create a custom field that will be used to modify the total price. We will create one for Client Invoice Lines.&lt;br /&gt;
&lt;br /&gt;
Go to the CustomFields administration panel, then into the Client Invoice Lines tab, then click on the button New Field, name the field &#039;&#039;&#039;coefficient&#039;&#039;&#039;, and set the type to &#039;&#039;&#039;Double&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
When you go to client invoices in Dolibarr, you should then see something similar to this:&lt;br /&gt;
[[File:Cfe1.jpg]]&lt;br /&gt;
You can see that our coefficient field was successfully created.&lt;br /&gt;
&lt;br /&gt;
Then here are two alternative ways of implementing the change of the total price of each product relativery to this coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change total price in database ==&lt;br /&gt;
&lt;br /&gt;
Here we will use a &amp;quot;save&amp;quot; overloading function to change the total price directly in the database, thus we won&#039;t have to modify anything else (eg: nor PDF templating nor ODT) since the total price will be already updated in the database.&lt;br /&gt;
&lt;br /&gt;
Warning: this method will work only with CustomFields =&amp;gt; 3.2.23 since the &amp;quot;save&amp;quot; overloading function did not work for products lines before.&lt;br /&gt;
&lt;br /&gt;
Just add the following overloading function in your customfields_fields_extend.lib.php file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_facturedet_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
 &lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $coefficient = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;coefficient&#039;}; // you could also use $coefficient = $value;&lt;br /&gt;
 &lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $coefficient = price2num($coefficient);&lt;br /&gt;
 &lt;br /&gt;
    // Get the original subprices (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
 &lt;br /&gt;
    // Update the subprices with the coefficient&lt;br /&gt;
    $object-&amp;gt;total_ht = $object-&amp;gt;total_ht * $coefficient; // compute no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva = $object-&amp;gt;total_tva * $coefficient; // compute tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc * $coefficient; // compute tax included price&lt;br /&gt;
 &lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(&#039;&#039;,true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
 &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;coefficient&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should show the correct total price on the Dolibarr&#039;s interface without modifying the unit subprice (P.U. Unit):&lt;br /&gt;
&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
You have nothing to edit in your PDF or ODT template, since the new subprice with applied coefficient will be automatically printed instead of the old one without coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change printed total price per product without tampering the subprice in the database ==&lt;br /&gt;
&lt;br /&gt;
Here is a way to change the printed total price (the one shown on screen and in PDF/ODT) but without changing the total price in the database.&lt;br /&gt;
&lt;br /&gt;
The following sub-chapters are all independent, thus you can only follow the ones that you require, eg: if you only need to change the price in your PDF documents, just create a custom field then jump to the sub-chapter to &#039;&#039;&#039;Implement the change of price in PDF&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in ODT templates ===&lt;br /&gt;
&lt;br /&gt;
There&#039;s no eval implementation currently inside ODT templates, but there is a kind of hook implementation, called [[Create_an_ODT_document_template#Other_personalized_tags|substitution functions]].&lt;br /&gt;
&lt;br /&gt;
You can either create your own module, then create your own substitution function, but for the sake of simplicity in this tutorial, we will directly use the CustomFields substitution function. However, please remember that you will have to redo your changes everytime you will update the CustomFields module, so if you don&#039;t want to do that it would be better if you create your own module and substitution function.&lt;br /&gt;
&lt;br /&gt;
Open the file &#039;&#039;&#039;htdocs/customfields/core/substitutions/functions_customfields.lib.php&#039;&#039;&#039; and go to the function &#039;&#039;&#039;customfields_completesubstitutionarray_lines&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
At the very end of this function, but inside the customfields check:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) {&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
But just before the ending bracket, you should add the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($object-&amp;gt;table_element_line === &#039;facturedet&#039;) { // First check that we are in the right module, we don&#039;t want to use coefficient for other modules if we didn&#039;t define the custom field there&lt;br /&gt;
    $substitutionarray[&#039;line_price_full&#039;] = $substitutionarray[&#039;line_price_ttc&#039;] * $substitutionarray[&#039;cf_coefficient&#039;]; // multiply the total price with our coefficient&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code will add a new tag &#039;&#039;&#039;{line_price_full}&#039;&#039;&#039; that you can then use inside your ODT templates.&lt;br /&gt;
&lt;br /&gt;
Just place this tag in a table in your ODT document, between the table tags ( [!-- BEGIN row.lines --] ... [!-- END row.lines --] ), and this will print your full price.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in PDF templates ===&lt;br /&gt;
&lt;br /&gt;
First, we will create our own PDF template by copying an existing template, and then we will edit it to change the price of items.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Creating your own PDF template by copying an existing template&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Go into the following folder: &#039;&#039;&#039;htdocs/core/modules/facture/doc/&#039;&#039;&#039;, then copy the file &#039;&#039;&#039;pdf_crabe.modules.php&#039;&#039;&#039; and name the new one &#039;&#039;&#039;pdf_crabecoeff.modules.php&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Then open it in your favourite editor, and change the following lines:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures // change pdf_crabe into pdf_crabecoeff&lt;br /&gt;
{&lt;br /&gt;
...&lt;br /&gt;
    function __construct($db)&lt;br /&gt;
    {&lt;br /&gt;
        ...&lt;br /&gt;
	$this-&amp;gt;name = &amp;quot;crabe&amp;quot;; // change &amp;quot;crabe&amp;quot; into &amp;quot;crabecoeff&amp;quot;&lt;br /&gt;
        ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you have your own PDF template!&lt;br /&gt;
&lt;br /&gt;
Now you should &#039;&#039;&#039;enable your PDF template&#039;&#039;&#039; before doing anything else, by going into the Modules Administration panel, and into the Invoice admin page, then click on the ON button at the right of the &#039;&#039;&#039;crabecoeff&#039;&#039;&#039; entry to enable the template.&lt;br /&gt;
&lt;br /&gt;
Now, let&#039;s proceed onto modifying the prices.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Changing the prices automatically in your PDF&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
You can now do pretty much anything you want in your PDF template, but I will give you a mean to automatically change the prices of every products with our coefficient, as well as the total price, without editing too much the template.&lt;br /&gt;
&lt;br /&gt;
First, try to find the following code of line (should be inside the write_file() method):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf=pdf_getInstance($this-&amp;gt;format);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, just copy/paste the following code &#039;&#039;&#039;below&#039;&#039;&#039; pdf_getInstance():&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$this-&amp;gt;customfields = customfields_fill_object($object, null, $outputlangs, null, true); // beautified values&lt;br /&gt;
$this-&amp;gt;customfields_raw = customfields_fill_object($object, null, $outputlangs, &#039;raw&#039;, null); // raw values&lt;br /&gt;
$this-&amp;gt;customfields_lines = customfields_fill_object_lines($object, null, $outputlangs, null, true); // product lines&#039; values&lt;br /&gt;
&lt;br /&gt;
// Resetting every $object price because we will compute them anew&lt;br /&gt;
$object-&amp;gt;total_ht = 0;&lt;br /&gt;
$object-&amp;gt;total_tva = 0;&lt;br /&gt;
$object-&amp;gt;total_ttc = 0;&lt;br /&gt;
// For each line of product/service, we will recompute all the prices&lt;br /&gt;
foreach($object-&amp;gt;lines as $line) {&lt;br /&gt;
    // Get the coefficient custom field&#039;s value for the current line&lt;br /&gt;
    $coeff = $object-&amp;gt;customfields-&amp;gt;lines-&amp;gt;{$line-&amp;gt;rowid}-&amp;gt;cf_coefficient;&lt;br /&gt;
    // Recompute the price of this product/service line&lt;br /&gt;
    $line-&amp;gt;total_ht = $line-&amp;gt;total_ht*$coeff; // compute no tax price&lt;br /&gt;
    $line-&amp;gt;total_tva = $line-&amp;gt;total_tva*$coeff; // compute tax amount&lt;br /&gt;
    $line-&amp;gt;total_ttc = $line-&amp;gt;total_ttc*$coeff; // compute tax included price&lt;br /&gt;
    // Do the sum of all new products/services prices and tva (will be incremented with every product/service line)&lt;br /&gt;
    $object-&amp;gt;total_ht += $line-&amp;gt;total_ht; // global no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva += $line-&amp;gt;total_tva; // global vat/tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc += $line-&amp;gt;total_ttc; // global  tax included price&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you should try to generate your PDF document, and you should get something like this:&lt;br /&gt;
[[File:Cfe3.jpg]]&lt;br /&gt;
&lt;br /&gt;
Note: printing the coefficient as a column in your PDF document is also possible but will not be covered here because PDF templates will vary for every persons depending on your needs.&lt;br /&gt;
For guidelines about how to do this, you can read the documentation on [[Create_a_PDF_document_template|how to create a PDF template]] and [[Module_CustomFields#Implementing_in_PDF_templates|how to use custom fields in PDF templates]].&lt;br /&gt;
&lt;br /&gt;
Hint: you should be looking inside the following conditional block, inside the write_file() method:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
{&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of total price in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
Unluckily there is not (yet) any hooking context to modify how lines of products/services are printed, thus the only thing you can do is directly edit the &#039;&#039;&#039;template file&#039;&#039;&#039; and manually put inside your code to load your custom field and print it the way you want using HTML formatting, for our example:&lt;br /&gt;
&lt;br /&gt;
* For Dolibarr 3.3 and above, edit the file &#039;&#039;&#039;htdocs/core/tpl/objectline_view.tpl.php&#039;&#039;&#039;&lt;br /&gt;
* For Dolibarr 3.1 and 3.2, edit the file &#039;&#039;&#039;htdocs/core/tpl/freeproductline_view.tpl.php&#039;&#039;&#039; AND &#039;&#039;&#039;predefinedproductline_view.tpl.php&#039;&#039;&#039; (apply the same changes to both files)&lt;br /&gt;
&lt;br /&gt;
In the template file you opened, find the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And replace it by:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $object-&amp;gt;table_element_line; // you can use $object-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
&lt;br /&gt;
// Modifying the HT price and printing it on the Dolibarr&#039;s datasheet interface&lt;br /&gt;
// Applying a multiplying/dividing coefficient to an HT price will give a mathematically correct TTC price since the taxes are also a multiplier&lt;br /&gt;
// However, if you want to apply other mathematical operations (like addition or substaction), you should use the total price with $line-&amp;gt;total_ttc&lt;br /&gt;
if (!empty($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
    echo price($line-&amp;gt;total_ht * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient);&lt;br /&gt;
} else {&lt;br /&gt;
    echo price($line-&amp;gt;total_ht);&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should produce something like this:&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Technical note: here we are using customfields_fill_object() instead of customfields_fill_object_lines() (the latter being specifically adapted to lines and thus should theoretically be used here) for performance reasons because the template file we are editing is called in a loop, and customfields_fill_object_lines() should be called outside a loop (because it already does the job that is done by the loop), so here we &#039;tricks&#039; the customfields_fill_object() function to avoid this loop by submitting &#039;facturedet&#039; as a module by itself (when in fact it&#039;s a product lines table, but it works the same way nevertheless). This is a good demonstration of the flexibility of the CustomFields functions and methods.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the coefficient column in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
It&#039;s also possible to show the coefficient in a new column on the Dolibarr interface.&lt;br /&gt;
&lt;br /&gt;
To do that, you need to do two things:&lt;br /&gt;
&lt;br /&gt;
1- Add the column title &amp;quot;Coeff.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
2- Add the generator that will input the coefficient value for each line.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Add the column title &amp;quot;Coeff.&amp;quot;&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
Open in your favourite editor the file /htdocs/core/class/commonobject.class.php and inside the function printObjectLines (note the plural s), find the following lines of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Total HT&lt;br /&gt;
print &#039;&amp;lt;td align=&amp;quot;right&amp;quot; width=&amp;quot;50&amp;quot;&amp;gt;&#039;.$langs-&amp;gt;trans(&#039;TotalHTShort&#039;).&#039;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is what prints the Total HT price column. We will place the Coeff column just before.&lt;br /&gt;
&lt;br /&gt;
So, just above this code, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
dol_include_once(&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element_line);&lt;br /&gt;
$coeff_field = $customfields-&amp;gt;fetchFieldStruct(&#039;coefficient&#039;);&lt;br /&gt;
if (!empty($coeff_field)) {&lt;br /&gt;
    print &#039;&amp;lt;td align=&amp;quot;right&amp;quot; width=&amp;quot;50&amp;quot;&amp;gt;&#039;.$langs-&amp;gt;trans(&#039;Coeff.&#039;).&#039;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will take care of showing the Coeff. column, but only for modules where you created such a custom field (because the file we modified is a common object for all modules in Dolibarr, not just invoices, thus this modification will gracefully skip this custom field if it does not exists for other modules).&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Add the generator&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/tpl/objectline_view.tpl.php and try to find the following block of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php if ($line-&amp;gt;special_code == 3)	{ ?&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo $langs-&amp;gt;trans(&#039;Option&#039;); ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } else { ?&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is what prints the total HT price value for each line.&lt;br /&gt;
&lt;br /&gt;
Just above of this block of code, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $object-&amp;gt;table_element_line; // you can use $this-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
if (isset($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
    &amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient; ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just like for the column title, this code will take care of generating the coefficient value only if the custom field is defined, because this file is also a generic template for all modules of Dolibarr.&lt;br /&gt;
&lt;br /&gt;
At this point, everything is done, you should see the following:&lt;br /&gt;
[[File:Cf_wiki_coeff_column.png]]&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in margin module ===&lt;br /&gt;
&lt;br /&gt;
If you enabled the margin module, you should see that the coefficient is not applied there, but it&#039;s easy to fix that.&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/class/commonobject.class.php and find the function getMarginInfos(), then inside find the following lines:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach($this-&amp;gt;lines as $line) {&lt;br /&gt;
    if (empty($line-&amp;gt;pa_ht) &amp;amp;&amp;amp; isset($line-&amp;gt;fk_fournprice) &amp;amp;&amp;amp; !$force_price) {&lt;br /&gt;
        $product = new ProductFournisseur($this-&amp;gt;db);&lt;br /&gt;
        if ($product-&amp;gt;fetch_product_fournisseur_price($line-&amp;gt;fk_fournprice))&lt;br /&gt;
            $line-&amp;gt;pa_ht = $product-&amp;gt;fourn_unitprice * (1 - $product-&amp;gt;fourn_remise_percent / 100);&lt;br /&gt;
        if (isset($conf-&amp;gt;global-&amp;gt;MARGIN_TYPE) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;MARGIN_TYPE == &amp;quot;2&amp;quot; &amp;amp;&amp;amp; $product-&amp;gt;fourn_unitcharges &amp;gt; 0)&lt;br /&gt;
            $line-&amp;gt;pa_ht += $product-&amp;gt;fourn_unitcharges;&lt;br /&gt;
    }&lt;br /&gt;
    // si prix d&#039;achat non renseigné et devrait l&#039;être, alors prix achat = prix vente&lt;br /&gt;
    if ((!isset($line-&amp;gt;pa_ht) || $line-&amp;gt;pa_ht == 0) &amp;amp;&amp;amp; $line-&amp;gt;subprice &amp;gt; 0 &amp;amp;&amp;amp; (isset($conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull == 1)) {&lt;br /&gt;
        $line-&amp;gt;pa_ht = $line-&amp;gt;subprice * (1 - ($line-&amp;gt;remise_percent / 100));&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the lines are filled with margin infos. The price has just been fetched here, and we will now modify it with the coefficient before the price gets used for margin computation.&lt;br /&gt;
&lt;br /&gt;
Just below the code we found, paste the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $line-&amp;gt;table_element;&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
if (!empty($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
    $line-&amp;gt;pa_ht = $line-&amp;gt;pa_ht * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient;&lt;br /&gt;
    $line-&amp;gt;subprice = $line-&amp;gt;subprice * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s all, the price should be reflected in the margin.&lt;br /&gt;
[[File:Cf_wiki_coeff_margin.png]]&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in remaining debt to pay ===&lt;br /&gt;
&lt;br /&gt;
Open /htdocs/core/class/commonobject.class.php and look for the function update_price().&lt;br /&gt;
&lt;br /&gt;
You should see the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function update_price($exclspec=0,$roundingadjust=&#039;none&#039;,$nodatabaseupdate=0,$seller=&#039;&#039;)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just below the include_once line, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;;&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/conf/conf_customfields_func.lib.php&#039;;&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $this-&amp;gt;table_element_line);&lt;br /&gt;
// Fetching line ids from parent object id&lt;br /&gt;
$line_ids = $customfields-&amp;gt;fetchAny(&#039;rowid&#039;, MAIN_DB_PREFIX.$this-&amp;gt;table_element_line, $this-&amp;gt;fk_element.&#039; = &#039;.$this-&amp;gt;id);&lt;br /&gt;
$line_ids = array_values_recursive(&#039;rowid&#039;, $line_ids);&lt;br /&gt;
// Load custom fields&lt;br /&gt;
$cf_records = $customfields-&amp;gt;fetch($line_ids);&lt;br /&gt;
// Reassociate the records with the line id (instead of the customfields record id)&lt;br /&gt;
$cf_line_cfrowid_to_rowid = array_reassociate(&#039;rowid&#039;, &#039;fk_&#039;.$this-&amp;gt;table_element_line, $cf_records);&lt;br /&gt;
$tmp = $cf_records;&lt;br /&gt;
$cf_records = array();&lt;br /&gt;
foreach ($tmp as $k=&amp;gt;$v) { $cf_records[$cf_line_cfrowid_to_rowid[$k]] = $tmp[$k]; }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will load the coefficient custom field (and also any other custom field you have defined for lines).&lt;br /&gt;
&lt;br /&gt;
Now scroll below, and you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
while ($i &amp;lt; $num)&lt;br /&gt;
{&lt;br /&gt;
    $obj = $this-&amp;gt;db-&amp;gt;fetch_object($resql);&lt;br /&gt;
&lt;br /&gt;
    // Note: There is no check on detail line and no check on total, if $forcedroundingmode = &#039;none&#039;&lt;br /&gt;
&lt;br /&gt;
    if ($forcedroundingmode == &#039;0&#039;)	// Check if data on line are consistent. This may solve lines that were not consistent because set with $forcedroundingmode=&#039;auto&#039;&lt;br /&gt;
    {&lt;br /&gt;
        $localtax_array=array($obj-&amp;gt;localtax1_type,$obj-&amp;gt;localtax1_tx,$obj-&amp;gt;localtax2_type,$obj-&amp;gt;localtax2_tx);&lt;br /&gt;
        $tmpcal=calcul_price_total($obj-&amp;gt;qty, $obj-&amp;gt;up, $obj-&amp;gt;remise_percent, $obj-&amp;gt;vatrate, $obj-&amp;gt;localtax1_tx, $obj-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $obj-&amp;gt;info_bits, $obj-&amp;gt;product_type, $seller, $localtax_array);&lt;br /&gt;
        $diff=price2num($tmpcal[1] - $obj-&amp;gt;total_tva, &#039;MT&#039;, 1);&lt;br /&gt;
        if ($diff)&lt;br /&gt;
        {&lt;br /&gt;
            $sqlfix=&amp;quot;UPDATE &amp;quot;.MAIN_DB_PREFIX.$this-&amp;gt;table_element_line.&amp;quot; SET &amp;quot;.$fieldtva.&amp;quot; = &amp;quot;.$tmpcal[1].&amp;quot;, total_ttc = &amp;quot;.$tmpcal[2].&amp;quot; WHERE rowid = &amp;quot;.$obj-&amp;gt;rowid;&lt;br /&gt;
            dol_syslog(&#039;We found unconsistent data into detailed line (difference of &#039;.$diff.&#039;) for line rowid = &#039;.$obj-&amp;gt;rowid.&amp;quot; (total vat of line calculated=&amp;quot;.$tmpcal[1].&amp;quot;, database=&amp;quot;.$obj-&amp;gt;total_tva.&amp;quot;). We fix the total_vat and total_ttc of line by running sqlfix = &amp;quot;.$sqlfix);&lt;br /&gt;
            $resqlfix=$this-&amp;gt;db-&amp;gt;query($sqlfix);&lt;br /&gt;
            if (! $resqlfix) dol_print_error($this-&amp;gt;db,&#039;Failed to update line&#039;);&lt;br /&gt;
            $obj-&amp;gt;total_tva = $tmpcal[1];&lt;br /&gt;
            $obj-&amp;gt;total_ttc = $tmpcal[2];&lt;br /&gt;
            //&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just after, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
$cf_coeff = $cf_records[$obj-&amp;gt;rowid]-&amp;gt;coefficient;&lt;br /&gt;
if (empty($cf_coeff) and $cf_coeff !== 0) $cf_coeff = 1; // by default, an empty coefficient = 1&lt;br /&gt;
$obj-&amp;gt;total_ht = $obj-&amp;gt;total_ht * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_tva = $obj-&amp;gt;total_tva * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_localtax1 = $obj-&amp;gt;total_localtax1 * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_localtax2 = $obj-&amp;gt;total_localtax2 * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_ttc = $obj-&amp;gt;total_ttc * $cf_coeff;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should do the trick. Now go to one of your invoices (or whatever module you are targetting), and try to add/edit a product/service line to force refresh the total price. You should then see this:&lt;br /&gt;
[[File:Cf_wiki_coeff_remaining_debt.png]]&lt;br /&gt;
&lt;br /&gt;
= Custom tax per product =&lt;br /&gt;
&lt;br /&gt;
This part will show you how to create a static custom tax per product. Static means that the tax will be stored per product, and not per product&#039;s line, thus you just have to setup the tax once per product and it will always be applied in every invoice you add this product.&lt;br /&gt;
&lt;br /&gt;
To do that, you will need the CustomFields Pro module + ProductsEasyAccess.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
1- Create a product&#039;s custom fields called &amp;quot;custom_tax&amp;quot;. Now, using ProductsEasyAccess, you can access this field wherever you wants, either to show it on the Dolibarr&#039;s interface (see previous chapter), or in a PDF template, which we will describe below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2- Modify your invoice&#039;s PDF template. You first need to load all the products&#039; fields &#039;&#039;before&#039;&#039; the loop over the products&#039; lines. To do this, search for a line like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// This should be placed above the &amp;quot;loop lines&amp;quot; below, add this:&lt;br /&gt;
&lt;br /&gt;
dol_include_once(&#039;/productseasyaccess/lib/productseasyaccess.lib.php&#039;);&lt;br /&gt;
fill_object_with_products_fields($object);&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines, search for this command&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  // Add this just below the for loop, this will ease your php manipulation after.&lt;br /&gt;
  // This will allow you to more easily access the products&#039; fields (this will store the id of each product inside $lineproductid).&lt;br /&gt;
  $line_product_id = $object-&amp;gt;lines[$i]-&amp;gt;fk_product;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4- You can now use your products&#039; fields in your PDF. To access the custom_tax field, just use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And to print it in your PDF:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell(0,3, $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax, 0, &#039;L&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5- Then, if you want to count the custom tax total and add it to your vat total, you can create a counter inside the for loop we cited in the first step:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_custom_tax = 0; // Initialize the custom tax total counter&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  $object-&amp;gt;total_custom_tax += $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then find inside the function **_tableau_tot**, find the following line:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And prepend the total modification like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc + $object-&amp;gt;total_custom_tax; // prepend this to add the custom tax to the total&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Done. This should show your custom tax per product and your total including all custom taxes.&lt;/div&gt;</summary>
		<author><name>Lrq3000</name></author>
	</entry>
	<entry>
		<id>https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33077</id>
		<title>Module CustomFields Cases</title>
		<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33077"/>
		<updated>2015-01-16T13:36:57Z</updated>

		<summary type="html">&lt;p&gt;Lrq3000: /* Implement the change of global price in most of Dolibarr (including remaining debt to pay and PDF and ODT) */ Added a note&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:CustomFields]]&lt;br /&gt;
{{TemplateDocDevEn}}&lt;br /&gt;
{{TemplateModEN}}&lt;br /&gt;
{{BasculeDevUserEn|&lt;br /&gt;
name=CustomFields|&lt;br /&gt;
num=8500|&lt;br /&gt;
devdoc=[[Module_CustomFields_Dev]]|&lt;br /&gt;
userdoc=[[Module_CustomFields]]|}}&lt;br /&gt;
This page lists a compilation of concrete, practical examples uses of [[Module_CustomFields|CustomFields]] and of its various features.&lt;br /&gt;
&lt;br /&gt;
If you had an experience with CustomFields, you can also add here your case as an example for others to get inspired.&lt;br /&gt;
&lt;br /&gt;
= How to make a generic PDF template listing all custom fields =&lt;br /&gt;
&lt;br /&gt;
To make a generic PDF template listing all custom fields, you just need to take an existing PDF template, then you need to follow these 3 steps:&lt;br /&gt;
&lt;br /&gt;
1/ Edit the class name (required, else it will conflict with the original template).&lt;br /&gt;
&lt;br /&gt;
Eg: if we take the crabe template for invoices, we change:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;crabe&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
into:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_customfields extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;customfields&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2/ Copy this generic function at the end of the PDF:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propale/order/etc... (CustomFields simpler functions will automatically adapt)&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs); // set default PDF font size&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
    // Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
    $customfields = customfields_fill_object($object, null, $outputlangs, null, true);&lt;br /&gt;
&lt;br /&gt;
    // Setting the starting position of the text cursor&lt;br /&gt;
    $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
    $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
    // Printing the customfields&lt;br /&gt;
    foreach ($object-&amp;gt;customfields as $label=&amp;gt;$value) { // $value is already formatted!&lt;br /&gt;
        // Get translated label&lt;br /&gt;
        $translatedlabel = $customfields-&amp;gt;findLabelPDF($label, $outputlangs); // translated label of the customfield (not translated by default in customfields_fill_object() because a field should always be accessible by a base name, whatever the translation is)&lt;br /&gt;
&lt;br /&gt;
        // PDF formatting, placement and printing&lt;br /&gt;
        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
        $pdf-&amp;gt;MultiCell(0,3, $translatedlabel.&#039; ($object-&amp;gt;customfields-&amp;gt;&#039;.$label.&#039;): &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternative function (with a bit less functionalities, but more controllable):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page (used to debug if CustomFields setup is correct)&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propal/product/whatever...&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs);&lt;br /&gt;
&lt;br /&gt;
            if (empty($object-&amp;gt;table_element) or empty($object-&amp;gt;id)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;Current \$object is not compatible with CustomFields, could not find table_element or id.&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
    $customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element);&lt;br /&gt;
&lt;br /&gt;
            // Fetching custom fields records&lt;br /&gt;
            $fields = $customfields-&amp;gt;fetch($object-&amp;gt;id);&lt;br /&gt;
&lt;br /&gt;
            if (!isset($fields)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;No custom field could be found for this object. Please check your configuration (did you create at least one customfield and set a value in the current datasheet?)&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            } else {&lt;br /&gt;
                // Setting the starting position of the text cursor&lt;br /&gt;
                $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
                $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
                // Printing the customfields&lt;br /&gt;
                foreach ($fields as $key=&amp;gt;$field) {&lt;br /&gt;
                        $translatedname = $customfields-&amp;gt;findLabelPDF($key, $outputlangs); // label of the customfield&lt;br /&gt;
                        $value = $customfields-&amp;gt;simpleprintFieldPDF($key, $field, $outputlangs); // value (cleaned and properly formatted) of the customfield&lt;br /&gt;
&lt;br /&gt;
                        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
                        $pdf-&amp;gt;MultiCell(0,3, $translatedname.&#039;: &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
                        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3/ Call the _pagecustomfields function at the right place (generally, after _pagefoot and just before $pdf-&amp;gt;Close()):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Pied de page&lt;br /&gt;
$this-&amp;gt;_pagefoot($pdf,$object,$outputlangs);&lt;br /&gt;
$pdf-&amp;gt;AliasNbPages();&lt;br /&gt;
&lt;br /&gt;
// CustomFields&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) { // if the customfields module is activated...&lt;br /&gt;
        // Add a page with the customfields&lt;br /&gt;
        $pdf-&amp;gt;AddPage();&lt;br /&gt;
        $this-&amp;gt;_pagecustomfields($pdf,$object,$outputlangs);&lt;br /&gt;
        $pagenb++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
$pdf-&amp;gt;Close(); // you should first search for this, you should only find one occurrence&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then go to the module&#039;s admin panel (eg: invoice admin panel) and enable the template you&#039;ve just created.&lt;br /&gt;
&lt;br /&gt;
You can now use it to test if your customfields work.&lt;br /&gt;
&lt;br /&gt;
= Conditions on fields selection =&lt;br /&gt;
&lt;br /&gt;
Sometimes you may want to show a DropdownBox or Constraint (or another multi-choice type), but you may want to show or hide choices based on conditions (eg: only if it&#039;s a prospect, only if this zone was selected, only if this category was selected, etc..).&lt;br /&gt;
&lt;br /&gt;
To know which is the best solution you should choose to implement your condition, you should first know in which category your condition falls in.&lt;br /&gt;
&lt;br /&gt;
== Conditions categories ==&lt;br /&gt;
&lt;br /&gt;
Conditions can be categorized into 4 types:&lt;br /&gt;
&lt;br /&gt;
* static condition: choices are restrained on a condition that never changes (eg: only show third-parties that are suppliers, or lowercase all data on sql insertion, or check that a field is above a certain number, etc.).&lt;br /&gt;
* semi-dynamic condition: choices that are constrained on a static table but based on the value of another field/customfield. This is also commonly called &amp;quot;Cascaded dropdown lists&amp;quot; or &amp;quot;Dynamically linked dropdown lists&amp;quot;.&lt;br /&gt;
* dynamic: everything is dynamic: choices are based on a subfield of a field of the current module, so that there&#039;s no material table and it&#039;s impossible to create a view (because you would need to create one view per id) (eg: show only the contacts attached to the third-party being accessed)&lt;br /&gt;
* timed condition: a condition that is based on time or recurrence (eg: at 5:00 AM delete old records, etc..)&lt;br /&gt;
&lt;br /&gt;
== Conditions solutions ==&lt;br /&gt;
&lt;br /&gt;
Now that you know what kind of condition you want to set, you can choose one of the following solutions depending on your preferences and knowledge.&lt;br /&gt;
&lt;br /&gt;
In any case, you can always use the &#039;&#039;&#039;CustomFields&#039;s overload functions&#039;&#039;&#039; to fit your needs for nearly every type of condition.&lt;br /&gt;
&lt;br /&gt;
=== Static condition ===&lt;br /&gt;
&lt;br /&gt;
* View: create a view on the table based on the condition, and then create a custom field constrained on this view (which is just like any table), eg: only show third-parties that are suppliers:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
CREATE VIEW llx_societe_supplier_view AS&lt;br /&gt;
SELECT *&lt;br /&gt;
FROM llx_societe&lt;br /&gt;
WHERE fournisseur=1;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
WARNING: foreign keys (and thus constraints) can&#039;t be used on views with MySQL, and materialized views neither exist, so you can&#039;t use this method with MySQL.&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
* SQL Transformations&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
=== Semi-dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade option is ideal in most cases, and if not enough, you can use the Cascade Custom option and Overloading Functions.&lt;br /&gt;
* View&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
&lt;br /&gt;
=== Dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade Custom and Overload functions&lt;br /&gt;
* Making your own module, which calls the CustomFields class&lt;br /&gt;
&lt;br /&gt;
=== Timed condition ===&lt;br /&gt;
&lt;br /&gt;
* SQL scheduled events&lt;br /&gt;
* Cron job&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
= Overloading functions =&lt;br /&gt;
&lt;br /&gt;
Here are a few concrete use cases of overloading functions to achieve various goals.&lt;br /&gt;
&lt;br /&gt;
== Dynamic list selection based on another one ==&lt;br /&gt;
&lt;br /&gt;
The goal is to show two custom fields on the Third-Party module: one which gives the zone (secteur) of the third-party (Isere, Alpes du sud, Haute-Savoie...) and the other which gives relative to the zone the list of all the ski resorts (station_a) inside the selected zone.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will show how to manually manage this kind of requirements using [[Module_CustomFields#Overloading_functions:_Adding_your_own_management_code_for_custom_fields|overloading functions]], but in latest CustomFields releases, the same solution can be achieved automatically using the [[Module_CustomFields#Cascade|Cascade option]].&lt;br /&gt;
&lt;br /&gt;
We have one table &#039;&#039;&#039;llx_k_station&#039;&#039;&#039; (rowid, station (char50), secteur(char50)) which contains the following: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1,les 2 alpes,isère&lt;br /&gt;
2,chamrousse,isère&lt;br /&gt;
3,alpe d&#039;huez,isère&lt;br /&gt;
4,vars,alpes du sud&lt;br /&gt;
5,risoul,alpes du sud&lt;br /&gt;
etc...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In CustomFields&#039;s admin panel, we create two custom fields:&lt;br /&gt;
&lt;br /&gt;
1- secteur: returns the list of all zones - type DropdownBox: enum(&#039;- Aucun&#039;,&#039;Alpes du Sud&#039;,&#039;export&#039;,&#039;Haute-Savoie&#039;,&#039;Isère&#039;, etc...)&lt;br /&gt;
&lt;br /&gt;
2- station_a: returns the list of all ski resorts - type constraint on &#039;&#039;&#039;llx_k_station&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Here is the code to put in customfields_fields_extend.lib.php that will allow to show only the ski resorts corresponding to the selected zone:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_editview_societe_station_a (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$parameters, &amp;amp;$action, &amp;amp;$user, &amp;amp;$idvar, &amp;amp;$rightok, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
  global $db; // allow to access database functions&lt;br /&gt;
  $sql=&amp;quot;SELECT llx_k_station.station, llx_k_station.rowid FROM llx_societe_customfields INNER JOIN llx_k_station ON llx_societe_customfields.Secteur = llx_k_station.secteur WHERE llx_societe_customfields.fk_societe=&amp;quot;.$object-&amp;gt;id;&lt;br /&gt;
$result=$db-&amp;gt;query($sql);&lt;br /&gt;
 &lt;br /&gt;
  if ($result) {&lt;br /&gt;
    $num = $db-&amp;gt;num_rows($result);&lt;br /&gt;
    $i = 0;&lt;br /&gt;
    $myarray = array();&lt;br /&gt;
    while ($i &amp;lt; $num)&lt;br /&gt;
    {&lt;br /&gt;
      $obj = $db-&amp;gt;fetch_object($result);&lt;br /&gt;
      $myarray []=array(&#039;id&#039;=&amp;gt;$obj-&amp;gt;rowid, &#039;value&#039;=&amp;gt;$obj-&amp;gt;station);&lt;br /&gt;
      $i++;&lt;br /&gt;
    }&lt;br /&gt;
    $value = $myarray; &lt;br /&gt;
    $db-&amp;gt;free($result);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to manub for giving this case.&lt;br /&gt;
&lt;br /&gt;
== Dynamic discount computation per products lines on propales ==&lt;br /&gt;
&lt;br /&gt;
The goal here is to compute the discount for a product line in propales based on a computation on three custom fields.&lt;br /&gt;
&lt;br /&gt;
First, create three custom fields for &amp;quot;Line commercial proposals&amp;quot;: vl, cpl and lc.&lt;br /&gt;
&lt;br /&gt;
Then add the following overloading function at the end of your customfields_fields_extend.lib.php file (read the comments for more informations):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_propaldet_vl (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
&lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $vl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;vl&#039;};&lt;br /&gt;
    $cpl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;cpl&#039;};&lt;br /&gt;
    $lc = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;lc&#039;};&lt;br /&gt;
&lt;br /&gt;
    // Compute the discount&lt;br /&gt;
    $remise_calculee = (100 - $vl) * (100 - $cpl) * (100 - $lc);&lt;br /&gt;
&lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $remise_calculee = price2num($remise_calculee);&lt;br /&gt;
&lt;br /&gt;
    // Assign the new discount into the PropalLine object&lt;br /&gt;
    $object-&amp;gt;remise_percent = $remise_calculee;&lt;br /&gt;
&lt;br /&gt;
    // Update the price (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
&lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
&lt;br /&gt;
    //$q = &amp;quot;UPDATE  &amp;quot;.MAIN_DB_PREFIX.&amp;quot;propaldet SET remise_percent=&amp;quot;.$remise_calculee.&amp;quot; WHERE  rowid=&amp;quot;.$object-&amp;gt;rowid;&lt;br /&gt;
    //$db-&amp;gt;query($q);&lt;br /&gt;
    &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;vl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;cpl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;lc&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Linking Dolibarr objects from two different modules =&lt;br /&gt;
&lt;br /&gt;
Dolibarr sometimes offers the possibility to link two different modules. Eg: when you convert an Intervention card into an Invoice.&lt;br /&gt;
&lt;br /&gt;
In this case, you might want in your templates to fetch the custom fields of the linked object. Eg: you generate a PDF from the Invoice, but you want to get the custom fields from the Intervention that was converted into the Invoice.&lt;br /&gt;
&lt;br /&gt;
The key here is to use the &#039;&#039;&#039;llx_element_element&#039;&#039;&#039; table or the &#039;&#039;&#039;llx_element_*&#039;&#039;&#039; tables (eg: llx_element_contact): this is a standard Dolibarr table that stores all links between objects of two different modules.&lt;br /&gt;
&lt;br /&gt;
Here is the structure:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
rowid | fk_source | sourcetype | fk_target | targettype&lt;br /&gt;
-------------------------------------------------------&lt;br /&gt;
1 | 2 | fichinter | 5 | facture&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can then use a php code like the following to populate your $object with customfields:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
// This will also create and returns us an instance of the CustomFields class, which we will use to manually query the llx_element_element table&lt;br /&gt;
$customfields = customfields_fill_object($object, null, $outputlangs, &#039;facture&#039;, true); // beautified values&lt;br /&gt;
&lt;br /&gt;
// Querying the llx_element_element table manually to get the link between the invoice and the linked intervention card (we want the id of the intervention card)&lt;br /&gt;
// fetchAny($columns, $table, $where=&#039;&#039;, $orderby=&#039;&#039;, $limitby=&#039;&#039;)&lt;br /&gt;
$eltrow = $customfields-&amp;gt;fetchAny(&#039;*&#039;, MAIN_DB_PREFIX.&#039;element_element&#039;, &#039;targettype=&amp;quot;&#039;.$object-&amp;gt;element.&#039;&amp;quot; and fk_target=&#039;.$object-&amp;gt;id.&#039; and sourcetype=&amp;quot;fichinter&amp;quot;&#039;);&lt;br /&gt;
&lt;br /&gt;
// Save the intervention&#039;s id&lt;br /&gt;
$sourceid = $eltrow[0][&#039;fk_source&#039;];&lt;br /&gt;
&lt;br /&gt;
// Crafting a dummy intervention object&lt;br /&gt;
$fromobject = new stdClass(); // Init an empty object&lt;br /&gt;
$fromobject-&amp;gt;id = $sourceid; // We need the id and..&lt;br /&gt;
$fromobject-&amp;gt;table_element = &#039;fichinter&#039;; // the table_element (name of the table)&lt;br /&gt;
&lt;br /&gt;
// We can then fill $object with the intervention custom fields (we store them in a sub property &#039;fichinter&#039; to avoid conflicts with invoice&#039;s custom fields)&lt;br /&gt;
customfields_fill_object($object, $fromobject, $outputlangs, &#039;fichinter&#039;, true);&lt;br /&gt;
&lt;br /&gt;
// Now you can access invoices custom fields using:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;facture-&amp;gt;cf_myfield);&lt;br /&gt;
// And access intervention custom fields with:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;fichinter-&amp;gt;cf_myfield);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to netassopro for the tip.&lt;br /&gt;
&lt;br /&gt;
= Modify the total global price with a coefficient (without tampering the discount) =&lt;br /&gt;
&lt;br /&gt;
This is more troublesome than modifying the total price per product/service line, because the total global price of an object (let&#039;s say an invoice, but this also works for any other object) is always recomputed by summing the prices and quantity of each product/service line. This means that although there&#039;s an entry &amp;quot;total_ttc&amp;quot; in the database to store the invoice&#039;s total price, this price is in fact always recomputed, it&#039;s never used as-is. Thus, it&#039;s useless to change only the database record: we need to inject some code to change how the total price is computed.&lt;br /&gt;
&lt;br /&gt;
This could be done using an overloading function or a trigger, but unluckily, Dolibarr doesn&#039;t call a trigger nor a hook when recomputing the price. We thus need to edit core files to do what we want.&lt;br /&gt;
&lt;br /&gt;
However, the bright side is that we can cleverly edit just two functions from one core file: update_price() and getMarginInfos() from /htdocs/core/class/commonobject.class.php , and everything should run smoothly. Beware that if you update Dolibarr, you will need to redo these two modifications in the code.&lt;br /&gt;
&lt;br /&gt;
First, you need to create a new custom field called &amp;quot;global_coefficient&amp;quot; in Invoices (not Invoices Lines!). Then you can continue to the subchapters below.&lt;br /&gt;
&lt;br /&gt;
Also note that this global coefficient is also compatible with per line coefficients (as described in the next chapter), thus you can use a global coefficient and per line coefficients at the same time. You can also use multiple global coefficient if you wish.&lt;br /&gt;
&lt;br /&gt;
Another note: if you have a grid of discounts depending on quantity or price or whatever parameter, you can program a precomputation code using a Custom Ajax function (this way, your discounts will be automatically computed, but then you can still manually enter them for particular cases since the custom field is editable).&lt;br /&gt;
&lt;br /&gt;
== Implement the change of global price in most of Dolibarr (including remaining debt to pay and PDF and ODT) ==&lt;br /&gt;
&lt;br /&gt;
The change we will implement here will reflect the new total price with the global coefficient in most of Dolibarr&#039;s code, since we will here change the main function that is usually called to update and get the total price.&lt;br /&gt;
&lt;br /&gt;
Open /htdocs/core/class/commonobject.class.php and look for the function update_price().&lt;br /&gt;
&lt;br /&gt;
You should see the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function update_price($exclspec=0,$roundingadjust=&#039;none&#039;,$nodatabaseupdate=0,$seller=&#039;&#039;)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just below the include_once line, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;;&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/conf/conf_customfields_func.lib.php&#039;;&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $this-&amp;gt;table_element);&lt;br /&gt;
// Get object&#039;s id (either stored in property &amp;quot;id&amp;quot; or &amp;quot;rowid&amp;quot;)&lt;br /&gt;
$this_id = (!empty($this-&amp;gt;id)) ? $this-&amp;gt;id : $this-&amp;gt;rowid;&lt;br /&gt;
// Load custom fields records&lt;br /&gt;
$cf_parent_record = $customfields-&amp;gt;fetch($this_id);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will load the global_coefficient custom field (and also any other custom field you have defined for invoices).&lt;br /&gt;
&lt;br /&gt;
Now scroll below, and you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
while ($i &amp;lt; $num)&lt;br /&gt;
{&lt;br /&gt;
    $obj = $this-&amp;gt;db-&amp;gt;fetch_object($resql);&lt;br /&gt;
&lt;br /&gt;
    ... (lots of code)&lt;br /&gt;
&lt;br /&gt;
    i++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Add revenue stamp to total&lt;br /&gt;
$this-&amp;gt;total_ttc       += isset($this-&amp;gt;revenuestamp)?$this-&amp;gt;revenuestamp:0;&lt;br /&gt;
&lt;br /&gt;
$this-&amp;gt;db-&amp;gt;free($resql);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is the end of the loop that is summing each product/service line price to compute the total price. Just after this loop, you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Now update global field total_ht, total_ttc and tva&lt;br /&gt;
$fieldht=&#039;total_ht&#039;;&lt;br /&gt;
$fieldtva=&#039;tva&#039;;&lt;br /&gt;
$fieldlocaltax1=&#039;localtax1&#039;;&lt;br /&gt;
$fieldlocaltax2=&#039;localtax2&#039;;&lt;br /&gt;
$fieldttc=&#039;total_ttc&#039;;&lt;br /&gt;
// Specific code for backward compatibility with old field names&lt;br /&gt;
if ($this-&amp;gt;element == &#039;facture&#039; || $this-&amp;gt;element == &#039;facturerec&#039;)             $fieldht=&#039;total&#039;;&lt;br /&gt;
if ($this-&amp;gt;element == &#039;facture_fourn&#039; || $this-&amp;gt;element == &#039;invoice_supplier&#039;) $fieldtva=&#039;total_tva&#039;;&lt;br /&gt;
if ($this-&amp;gt;element == &#039;propal&#039;)                                                $fieldttc=&#039;total&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Between those two blocks of code (so just above // Now update global field total_ht, total_ttc and tva), you can place the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
if (!empty($cf_parent_record-&amp;gt;global_coefficient)) { // avoid applying the coefficient if empty (this will return a 0 price)&lt;br /&gt;
    $this-&amp;gt;total_ht = $this-&amp;gt;total_ht * $cf_parent_record-&amp;gt;global_coefficient;&lt;br /&gt;
    $this-&amp;gt;total_tva = $this-&amp;gt;total_tva * $cf_parent_record-&amp;gt;global_coefficient;&lt;br /&gt;
    $this-&amp;gt;total_ttc = $this-&amp;gt;total_ttc * $cf_parent_record-&amp;gt;global_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Done, now try to add/edit a product/service line, and the total price should be affected by the global coefficient.&lt;br /&gt;
&lt;br /&gt;
Note that this modification &#039;&#039;&#039;needs only to be done once for all modules&#039;&#039;&#039;, you then just have to create a custom field named &amp;quot;global_coefficient&amp;quot; in every module you want to apply this coefficient.&lt;br /&gt;
&lt;br /&gt;
== Implementing the change of price in margin module ==&lt;br /&gt;
&lt;br /&gt;
If you enabled the margin module, you should see that the coefficient is not applied there, but it&#039;s easy to fix that.&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/class/commonobject.class.php and find the function getMarginInfos(), then inside find the following lines:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach($this-&amp;gt;lines as $line) {&lt;br /&gt;
    if (empty($line-&amp;gt;pa_ht) &amp;amp;&amp;amp; isset($line-&amp;gt;fk_fournprice) &amp;amp;&amp;amp; !$force_price) {&lt;br /&gt;
        $product = new ProductFournisseur($this-&amp;gt;db);&lt;br /&gt;
        if ($product-&amp;gt;fetch_product_fournisseur_price($line-&amp;gt;fk_fournprice))&lt;br /&gt;
            $line-&amp;gt;pa_ht = $product-&amp;gt;fourn_unitprice * (1 - $product-&amp;gt;fourn_remise_percent / 100);&lt;br /&gt;
        if (isset($conf-&amp;gt;global-&amp;gt;MARGIN_TYPE) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;MARGIN_TYPE == &amp;quot;2&amp;quot; &amp;amp;&amp;amp; $product-&amp;gt;fourn_unitcharges &amp;gt; 0)&lt;br /&gt;
            $line-&amp;gt;pa_ht += $product-&amp;gt;fourn_unitcharges;&lt;br /&gt;
    }&lt;br /&gt;
    // si prix d&#039;achat non renseigné et devrait l&#039;être, alors prix achat = prix vente&lt;br /&gt;
    if ((!isset($line-&amp;gt;pa_ht) || $line-&amp;gt;pa_ht == 0) &amp;amp;&amp;amp; $line-&amp;gt;subprice &amp;gt; 0 &amp;amp;&amp;amp; (isset($conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull == 1)) {&lt;br /&gt;
        $line-&amp;gt;pa_ht = $line-&amp;gt;subprice * (1 - ($line-&amp;gt;remise_percent / 100));&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the lines are filled with margin infos. The price has just been fetched here, and we will now modify it with the coefficient before the price gets used for margin computation.&lt;br /&gt;
&lt;br /&gt;
To optimize things up, we will load the custom fields before the loop (because they don&#039;t change between product/service lines, they are the custom fields of the parent object), and then apply the global_coefficient inside the loop.&lt;br /&gt;
&lt;br /&gt;
Just above the code we found (just before the for loop), paste the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
// Get object&#039;s id (either stored in property &amp;quot;id&amp;quot; or &amp;quot;rowid&amp;quot;)&lt;br /&gt;
$this_id = (!empty($this-&amp;gt;id)) ? $this-&amp;gt;id : $this-&amp;gt;rowid;&lt;br /&gt;
// Load custom fields records&lt;br /&gt;
$customfields = customfields_fill_object($this);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And now, just below the code we found, inside the loop (and just above the comment: &amp;quot;// calcul des marges&amp;quot;), paste the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
// Apply the coefficient&lt;br /&gt;
if (!empty($this-&amp;gt;customfields-&amp;gt;cf_global_coefficient)) {&lt;br /&gt;
    $line-&amp;gt;pa_ht = $line-&amp;gt;pa_ht * $this-&amp;gt;customfields-&amp;gt;cf_global_coefficient;&lt;br /&gt;
    $line-&amp;gt;subprice = $line-&amp;gt;subprice * $this-&amp;gt;customfields-&amp;gt;cf_global_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that here we cheated a bit: we do not compute the coefficient on the global total price, but rather per product/service line price. This is done on purpose, because mathematically this should produce no difference, and it also allows Dolibarr to continue peacefully with its margin calculations (else we would have to adapt all those computations, which would imply a lot more of code changes!).&lt;br /&gt;
&lt;br /&gt;
That&#039;s all, the price should be reflected in the margin. Just refresh the page to reflect the change.&lt;br /&gt;
&lt;br /&gt;
== Refresh the price instantly ==&lt;br /&gt;
&lt;br /&gt;
Maybe you noticed one glitch: when you modify the global_coefficient, the global total price isn&#039;t updated until you add/edit a product/service line. This is because the update_price() function is only called when we add/edit a product/service line.&lt;br /&gt;
&lt;br /&gt;
To fix that, we need now to force the refreshing of price. To do that, we will use an &amp;quot;aftersave&amp;quot; overloading function, which will execute commands after our custom field is saved.&lt;br /&gt;
&lt;br /&gt;
Go to the folder /htdocs/customfields/fields and rename the file customfields_fields_extend.lib.php.example into customfields_fields_extend.lib.php .&lt;br /&gt;
&lt;br /&gt;
Now, open it in your favourite text editor, and add the following function :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_aftersave_facture_global_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value, $fields) {&lt;br /&gt;
    // Force refreshing total price&lt;br /&gt;
    $object-&amp;gt;parent_object-&amp;gt;update_price(0, &#039;auto&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s all. Now, when you edit your global_coefficient, this will force refresh the total price.&lt;br /&gt;
&lt;br /&gt;
However, you will still notice one last glitch: when you edit the global_coefficient, the total price isn&#039;t reflected instantly, you must refresh the webpage once to see the new price. This is because of how the datasheet and the CustomFields hook are designed: CustomFields is called only exactly where the custom fields are place, thus it is called after the total price, the margin and the remaining debt are printed. Thus, CustomFields updates the value of your custom field only after all those informations get printed, and thus it cannot modify them at this stage. That&#039;s why those information are out-of-date, and you must refresh the webpage once to get to see the latest values.&lt;br /&gt;
&lt;br /&gt;
There is nothing you can do about it. One way to fix that would be to modify how CustomFields save the custom fields, by moving this stage at the very top. However, this would need another hook, which should be called at the very beginning of the page loading, which should be made only for this purpose (PageLoading), and should be available in all modules supported by CustomFields in order to not break anything.&lt;br /&gt;
&lt;br /&gt;
= Modify the total price PER product/service line with a coefficient (without tampering the discount) =&lt;br /&gt;
&lt;br /&gt;
Here we will go through the creation and management of a custom field called &#039;&#039;&#039;coefficient&#039;&#039;&#039; that will change the total price of each product by modifying it by the coefficient.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will not create a coefficient per product but rather per product&#039;s line. Thus if you want to create a coefficient that never change between invoices but only depending on the product and stays static, please refer to the next chapter below about creating a custom tax per product.&lt;br /&gt;
&lt;br /&gt;
The preliminary and necessary step is to first create a custom field that will be used to modify the total price. We will create one for Client Invoice Lines.&lt;br /&gt;
&lt;br /&gt;
Go to the CustomFields administration panel, then into the Client Invoice Lines tab, then click on the button New Field, name the field &#039;&#039;&#039;coefficient&#039;&#039;&#039;, and set the type to &#039;&#039;&#039;Double&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
When you go to client invoices in Dolibarr, you should then see something similar to this:&lt;br /&gt;
[[File:Cfe1.jpg]]&lt;br /&gt;
You can see that our coefficient field was successfully created.&lt;br /&gt;
&lt;br /&gt;
Then here are two alternative ways of implementing the change of the total price of each product relativery to this coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change total price in database ==&lt;br /&gt;
&lt;br /&gt;
Here we will use a &amp;quot;save&amp;quot; overloading function to change the total price directly in the database, thus we won&#039;t have to modify anything else (eg: nor PDF templating nor ODT) since the total price will be already updated in the database.&lt;br /&gt;
&lt;br /&gt;
Warning: this method will work only with CustomFields =&amp;gt; 3.2.23 since the &amp;quot;save&amp;quot; overloading function did not work for products lines before.&lt;br /&gt;
&lt;br /&gt;
Just add the following overloading function in your customfields_fields_extend.lib.php file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_facturedet_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
 &lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $coefficient = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;coefficient&#039;}; // you could also use $coefficient = $value;&lt;br /&gt;
 &lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $coefficient = price2num($coefficient);&lt;br /&gt;
 &lt;br /&gt;
    // Get the original subprices (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
 &lt;br /&gt;
    // Update the subprices with the coefficient&lt;br /&gt;
    $object-&amp;gt;total_ht = $object-&amp;gt;total_ht * $coefficient; // compute no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva = $object-&amp;gt;total_tva * $coefficient; // compute tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc * $coefficient; // compute tax included price&lt;br /&gt;
 &lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(&#039;&#039;,true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
 &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;coefficient&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should show the correct total price on the Dolibarr&#039;s interface without modifying the unit subprice (P.U. Unit):&lt;br /&gt;
&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
You have nothing to edit in your PDF or ODT template, since the new subprice with applied coefficient will be automatically printed instead of the old one without coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change printed total price per product without tampering the subprice in the database ==&lt;br /&gt;
&lt;br /&gt;
Here is a way to change the printed total price (the one shown on screen and in PDF/ODT) but without changing the total price in the database.&lt;br /&gt;
&lt;br /&gt;
The following sub-chapters are all independent, thus you can only follow the ones that you require, eg: if you only need to change the price in your PDF documents, just create a custom field then jump to the sub-chapter to &#039;&#039;&#039;Implement the change of price in PDF&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in ODT templates ===&lt;br /&gt;
&lt;br /&gt;
There&#039;s no eval implementation currently inside ODT templates, but there is a kind of hook implementation, called [[Create_an_ODT_document_template#Other_personalized_tags|substitution functions]].&lt;br /&gt;
&lt;br /&gt;
You can either create your own module, then create your own substitution function, but for the sake of simplicity in this tutorial, we will directly use the CustomFields substitution function. However, please remember that you will have to redo your changes everytime you will update the CustomFields module, so if you don&#039;t want to do that it would be better if you create your own module and substitution function.&lt;br /&gt;
&lt;br /&gt;
Open the file &#039;&#039;&#039;htdocs/customfields/core/substitutions/functions_customfields.lib.php&#039;&#039;&#039; and go to the function &#039;&#039;&#039;customfields_completesubstitutionarray_lines&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
At the very end of this function, but inside the customfields check:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) {&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
But just before the ending bracket, you should add the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($object-&amp;gt;table_element_line === &#039;facturedet&#039;) { // First check that we are in the right module, we don&#039;t want to use coefficient for other modules if we didn&#039;t define the custom field there&lt;br /&gt;
    $substitutionarray[&#039;line_price_full&#039;] = $substitutionarray[&#039;line_price_ttc&#039;] * $substitutionarray[&#039;cf_coefficient&#039;]; // multiply the total price with our coefficient&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code will add a new tag &#039;&#039;&#039;{line_price_full}&#039;&#039;&#039; that you can then use inside your ODT templates.&lt;br /&gt;
&lt;br /&gt;
Just place this tag in a table in your ODT document, between the table tags ( [!-- BEGIN row.lines --] ... [!-- END row.lines --] ), and this will print your full price.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in PDF templates ===&lt;br /&gt;
&lt;br /&gt;
First, we will create our own PDF template by copying an existing template, and then we will edit it to change the price of items.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Creating your own PDF template by copying an existing template&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Go into the following folder: &#039;&#039;&#039;htdocs/core/modules/facture/doc/&#039;&#039;&#039;, then copy the file &#039;&#039;&#039;pdf_crabe.modules.php&#039;&#039;&#039; and name the new one &#039;&#039;&#039;pdf_crabecoeff.modules.php&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Then open it in your favourite editor, and change the following lines:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures // change pdf_crabe into pdf_crabecoeff&lt;br /&gt;
{&lt;br /&gt;
...&lt;br /&gt;
    function __construct($db)&lt;br /&gt;
    {&lt;br /&gt;
        ...&lt;br /&gt;
	$this-&amp;gt;name = &amp;quot;crabe&amp;quot;; // change &amp;quot;crabe&amp;quot; into &amp;quot;crabecoeff&amp;quot;&lt;br /&gt;
        ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you have your own PDF template!&lt;br /&gt;
&lt;br /&gt;
Now you should &#039;&#039;&#039;enable your PDF template&#039;&#039;&#039; before doing anything else, by going into the Modules Administration panel, and into the Invoice admin page, then click on the ON button at the right of the &#039;&#039;&#039;crabecoeff&#039;&#039;&#039; entry to enable the template.&lt;br /&gt;
&lt;br /&gt;
Now, let&#039;s proceed onto modifying the prices.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Changing the prices automatically in your PDF&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
You can now do pretty much anything you want in your PDF template, but I will give you a mean to automatically change the prices of every products with our coefficient, as well as the total price, without editing too much the template.&lt;br /&gt;
&lt;br /&gt;
First, try to find the following code of line (should be inside the write_file() method):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf=pdf_getInstance($this-&amp;gt;format);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, just copy/paste the following code &#039;&#039;&#039;below&#039;&#039;&#039; pdf_getInstance():&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$this-&amp;gt;customfields = customfields_fill_object($object, null, $outputlangs, null, true); // beautified values&lt;br /&gt;
$this-&amp;gt;customfields_raw = customfields_fill_object($object, null, $outputlangs, &#039;raw&#039;, null); // raw values&lt;br /&gt;
$this-&amp;gt;customfields_lines = customfields_fill_object_lines($object, null, $outputlangs, null, true); // product lines&#039; values&lt;br /&gt;
&lt;br /&gt;
// Resetting every $object price because we will compute them anew&lt;br /&gt;
$object-&amp;gt;total_ht = 0;&lt;br /&gt;
$object-&amp;gt;total_tva = 0;&lt;br /&gt;
$object-&amp;gt;total_ttc = 0;&lt;br /&gt;
// For each line of product/service, we will recompute all the prices&lt;br /&gt;
foreach($object-&amp;gt;lines as $line) {&lt;br /&gt;
    // Get the coefficient custom field&#039;s value for the current line&lt;br /&gt;
    $coeff = $object-&amp;gt;customfields-&amp;gt;lines-&amp;gt;{$line-&amp;gt;rowid}-&amp;gt;cf_coefficient;&lt;br /&gt;
    // Recompute the price of this product/service line&lt;br /&gt;
    $line-&amp;gt;total_ht = $line-&amp;gt;total_ht*$coeff; // compute no tax price&lt;br /&gt;
    $line-&amp;gt;total_tva = $line-&amp;gt;total_tva*$coeff; // compute tax amount&lt;br /&gt;
    $line-&amp;gt;total_ttc = $line-&amp;gt;total_ttc*$coeff; // compute tax included price&lt;br /&gt;
    // Do the sum of all new products/services prices and tva (will be incremented with every product/service line)&lt;br /&gt;
    $object-&amp;gt;total_ht += $line-&amp;gt;total_ht; // global no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva += $line-&amp;gt;total_tva; // global vat/tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc += $line-&amp;gt;total_ttc; // global  tax included price&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you should try to generate your PDF document, and you should get something like this:&lt;br /&gt;
[[File:Cfe3.jpg]]&lt;br /&gt;
&lt;br /&gt;
Note: printing the coefficient as a column in your PDF document is also possible but will not be covered here because PDF templates will vary for every persons depending on your needs.&lt;br /&gt;
For guidelines about how to do this, you can read the documentation on [[Create_a_PDF_document_template|how to create a PDF template]] and [[Module_CustomFields#Implementing_in_PDF_templates|how to use custom fields in PDF templates]].&lt;br /&gt;
&lt;br /&gt;
Hint: you should be looking inside the following conditional block, inside the write_file() method:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
{&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of total price in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
Unluckily there is not (yet) any hooking context to modify how lines of products/services are printed, thus the only thing you can do is directly edit the &#039;&#039;&#039;template file&#039;&#039;&#039; and manually put inside your code to load your custom field and print it the way you want using HTML formatting, for our example:&lt;br /&gt;
&lt;br /&gt;
* For Dolibarr 3.3 and above, edit the file &#039;&#039;&#039;htdocs/core/tpl/objectline_view.tpl.php&#039;&#039;&#039;&lt;br /&gt;
* For Dolibarr 3.1 and 3.2, edit the file &#039;&#039;&#039;htdocs/core/tpl/freeproductline_view.tpl.php&#039;&#039;&#039; AND &#039;&#039;&#039;predefinedproductline_view.tpl.php&#039;&#039;&#039; (apply the same changes to both files)&lt;br /&gt;
&lt;br /&gt;
In the template file you opened, find the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And replace it by:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $object-&amp;gt;table_element_line; // you can use $object-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
&lt;br /&gt;
// Modifying the HT price and printing it on the Dolibarr&#039;s datasheet interface&lt;br /&gt;
// Applying a multiplying/dividing coefficient to an HT price will give a mathematically correct TTC price since the taxes are also a multiplier&lt;br /&gt;
// However, if you want to apply other mathematical operations (like addition or substaction), you should use the total price with $line-&amp;gt;total_ttc&lt;br /&gt;
if (!empty($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
    echo price($line-&amp;gt;total_ht * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient);&lt;br /&gt;
} else {&lt;br /&gt;
    echo price($line-&amp;gt;total_ht);&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should produce something like this:&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Technical note: here we are using customfields_fill_object() instead of customfields_fill_object_lines() (the latter being specifically adapted to lines and thus should theoretically be used here) for performance reasons because the template file we are editing is called in a loop, and customfields_fill_object_lines() should be called outside a loop (because it already does the job that is done by the loop), so here we &#039;tricks&#039; the customfields_fill_object() function to avoid this loop by submitting &#039;facturedet&#039; as a module by itself (when in fact it&#039;s a product lines table, but it works the same way nevertheless). This is a good demonstration of the flexibility of the CustomFields functions and methods.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the coefficient column in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
It&#039;s also possible to show the coefficient in a new column on the Dolibarr interface.&lt;br /&gt;
&lt;br /&gt;
To do that, you need to do two things:&lt;br /&gt;
&lt;br /&gt;
1- Add the column title &amp;quot;Coeff.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
2- Add the generator that will input the coefficient value for each line.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Add the column title &amp;quot;Coeff.&amp;quot;&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
Open in your favourite editor the file /htdocs/core/class/commonobject.class.php and inside the function printObjectLines (note the plural s), find the following lines of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Total HT&lt;br /&gt;
print &#039;&amp;lt;td align=&amp;quot;right&amp;quot; width=&amp;quot;50&amp;quot;&amp;gt;&#039;.$langs-&amp;gt;trans(&#039;TotalHTShort&#039;).&#039;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is what prints the Total HT price column. We will place the Coeff column just before.&lt;br /&gt;
&lt;br /&gt;
So, just above this code, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
dol_include_once(&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element_line);&lt;br /&gt;
$coeff_field = $customfields-&amp;gt;fetchFieldStruct(&#039;coefficient&#039;);&lt;br /&gt;
if (!empty($coeff_field)) {&lt;br /&gt;
    print &#039;&amp;lt;td align=&amp;quot;right&amp;quot; width=&amp;quot;50&amp;quot;&amp;gt;&#039;.$langs-&amp;gt;trans(&#039;Coeff.&#039;).&#039;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will take care of showing the Coeff. column, but only for modules where you created such a custom field (because the file we modified is a common object for all modules in Dolibarr, not just invoices, thus this modification will gracefully skip this custom field if it does not exists for other modules).&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Add the generator&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/tpl/objectline_view.tpl.php and try to find the following block of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php if ($line-&amp;gt;special_code == 3)	{ ?&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo $langs-&amp;gt;trans(&#039;Option&#039;); ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } else { ?&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is what prints the total HT price value for each line.&lt;br /&gt;
&lt;br /&gt;
Just above of this block of code, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $object-&amp;gt;table_element_line; // you can use $this-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
if (isset($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
    &amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient; ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just like for the column title, this code will take care of generating the coefficient value only if the custom field is defined, because this file is also a generic template for all modules of Dolibarr.&lt;br /&gt;
&lt;br /&gt;
At this point, everything is done, you should see the following:&lt;br /&gt;
[[File:Cf_wiki_coeff_column.png]]&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in margin module ===&lt;br /&gt;
&lt;br /&gt;
If you enabled the margin module, you should see that the coefficient is not applied there, but it&#039;s easy to fix that.&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/class/commonobject.class.php and find the function getMarginInfos(), then inside find the following lines:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach($this-&amp;gt;lines as $line) {&lt;br /&gt;
    if (empty($line-&amp;gt;pa_ht) &amp;amp;&amp;amp; isset($line-&amp;gt;fk_fournprice) &amp;amp;&amp;amp; !$force_price) {&lt;br /&gt;
        $product = new ProductFournisseur($this-&amp;gt;db);&lt;br /&gt;
        if ($product-&amp;gt;fetch_product_fournisseur_price($line-&amp;gt;fk_fournprice))&lt;br /&gt;
            $line-&amp;gt;pa_ht = $product-&amp;gt;fourn_unitprice * (1 - $product-&amp;gt;fourn_remise_percent / 100);&lt;br /&gt;
        if (isset($conf-&amp;gt;global-&amp;gt;MARGIN_TYPE) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;MARGIN_TYPE == &amp;quot;2&amp;quot; &amp;amp;&amp;amp; $product-&amp;gt;fourn_unitcharges &amp;gt; 0)&lt;br /&gt;
            $line-&amp;gt;pa_ht += $product-&amp;gt;fourn_unitcharges;&lt;br /&gt;
    }&lt;br /&gt;
    // si prix d&#039;achat non renseigné et devrait l&#039;être, alors prix achat = prix vente&lt;br /&gt;
    if ((!isset($line-&amp;gt;pa_ht) || $line-&amp;gt;pa_ht == 0) &amp;amp;&amp;amp; $line-&amp;gt;subprice &amp;gt; 0 &amp;amp;&amp;amp; (isset($conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull == 1)) {&lt;br /&gt;
        $line-&amp;gt;pa_ht = $line-&amp;gt;subprice * (1 - ($line-&amp;gt;remise_percent / 100));&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the lines are filled with margin infos. The price has just been fetched here, and we will now modify it with the coefficient before the price gets used for margin computation.&lt;br /&gt;
&lt;br /&gt;
Just below the code we found, paste the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $line-&amp;gt;table_element;&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
if (!empty($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
    $line-&amp;gt;pa_ht = $line-&amp;gt;pa_ht * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient;&lt;br /&gt;
    $line-&amp;gt;subprice = $line-&amp;gt;subprice * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s all, the price should be reflected in the margin.&lt;br /&gt;
[[File:Cf_wiki_coeff_margin.png]]&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in remaining debt to pay ===&lt;br /&gt;
&lt;br /&gt;
Open /htdocs/core/class/commonobject.class.php and look for the function update_price().&lt;br /&gt;
&lt;br /&gt;
You should see the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function update_price($exclspec=0,$roundingadjust=&#039;none&#039;,$nodatabaseupdate=0,$seller=&#039;&#039;)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just below the include_once line, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;;&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/conf/conf_customfields_func.lib.php&#039;;&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $this-&amp;gt;table_element_line);&lt;br /&gt;
// Fetching line ids from parent object id&lt;br /&gt;
$line_ids = $customfields-&amp;gt;fetchAny(&#039;rowid&#039;, MAIN_DB_PREFIX.$this-&amp;gt;table_element_line, $this-&amp;gt;fk_element.&#039; = &#039;.$this-&amp;gt;id);&lt;br /&gt;
$line_ids = array_values_recursive(&#039;rowid&#039;, $line_ids);&lt;br /&gt;
// Load custom fields&lt;br /&gt;
$cf_records = $customfields-&amp;gt;fetch($line_ids);&lt;br /&gt;
// Reassociate the records with the line id (instead of the customfields record id)&lt;br /&gt;
$cf_line_cfrowid_to_rowid = array_reassociate(&#039;rowid&#039;, &#039;fk_&#039;.$this-&amp;gt;table_element_line, $cf_records);&lt;br /&gt;
$tmp = $cf_records;&lt;br /&gt;
$cf_records = array();&lt;br /&gt;
foreach ($tmp as $k=&amp;gt;$v) { $cf_records[$cf_line_cfrowid_to_rowid[$k]] = $tmp[$k]; }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will load the coefficient custom field (and also any other custom field you have defined for lines).&lt;br /&gt;
&lt;br /&gt;
Now scroll below, and you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
while ($i &amp;lt; $num)&lt;br /&gt;
{&lt;br /&gt;
    $obj = $this-&amp;gt;db-&amp;gt;fetch_object($resql);&lt;br /&gt;
&lt;br /&gt;
    // Note: There is no check on detail line and no check on total, if $forcedroundingmode = &#039;none&#039;&lt;br /&gt;
&lt;br /&gt;
    if ($forcedroundingmode == &#039;0&#039;)	// Check if data on line are consistent. This may solve lines that were not consistent because set with $forcedroundingmode=&#039;auto&#039;&lt;br /&gt;
    {&lt;br /&gt;
        $localtax_array=array($obj-&amp;gt;localtax1_type,$obj-&amp;gt;localtax1_tx,$obj-&amp;gt;localtax2_type,$obj-&amp;gt;localtax2_tx);&lt;br /&gt;
        $tmpcal=calcul_price_total($obj-&amp;gt;qty, $obj-&amp;gt;up, $obj-&amp;gt;remise_percent, $obj-&amp;gt;vatrate, $obj-&amp;gt;localtax1_tx, $obj-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $obj-&amp;gt;info_bits, $obj-&amp;gt;product_type, $seller, $localtax_array);&lt;br /&gt;
        $diff=price2num($tmpcal[1] - $obj-&amp;gt;total_tva, &#039;MT&#039;, 1);&lt;br /&gt;
        if ($diff)&lt;br /&gt;
        {&lt;br /&gt;
            $sqlfix=&amp;quot;UPDATE &amp;quot;.MAIN_DB_PREFIX.$this-&amp;gt;table_element_line.&amp;quot; SET &amp;quot;.$fieldtva.&amp;quot; = &amp;quot;.$tmpcal[1].&amp;quot;, total_ttc = &amp;quot;.$tmpcal[2].&amp;quot; WHERE rowid = &amp;quot;.$obj-&amp;gt;rowid;&lt;br /&gt;
            dol_syslog(&#039;We found unconsistent data into detailed line (difference of &#039;.$diff.&#039;) for line rowid = &#039;.$obj-&amp;gt;rowid.&amp;quot; (total vat of line calculated=&amp;quot;.$tmpcal[1].&amp;quot;, database=&amp;quot;.$obj-&amp;gt;total_tva.&amp;quot;). We fix the total_vat and total_ttc of line by running sqlfix = &amp;quot;.$sqlfix);&lt;br /&gt;
            $resqlfix=$this-&amp;gt;db-&amp;gt;query($sqlfix);&lt;br /&gt;
            if (! $resqlfix) dol_print_error($this-&amp;gt;db,&#039;Failed to update line&#039;);&lt;br /&gt;
            $obj-&amp;gt;total_tva = $tmpcal[1];&lt;br /&gt;
            $obj-&amp;gt;total_ttc = $tmpcal[2];&lt;br /&gt;
            //&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just after, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
$cf_coeff = $cf_records[$obj-&amp;gt;rowid]-&amp;gt;coefficient;&lt;br /&gt;
if (empty($cf_coeff) and $cf_coeff !== 0) $cf_coeff = 1; // by default, an empty coefficient = 1&lt;br /&gt;
$obj-&amp;gt;total_ht = $obj-&amp;gt;total_ht * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_tva = $obj-&amp;gt;total_tva * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_localtax1 = $obj-&amp;gt;total_localtax1 * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_localtax2 = $obj-&amp;gt;total_localtax2 * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_ttc = $obj-&amp;gt;total_ttc * $cf_coeff;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should do the trick. Now go to one of your invoices (or whatever module you are targetting), and try to add/edit a product/service line to force refresh the total price. You should then see this:&lt;br /&gt;
[[File:Cf_wiki_coeff_remaining_debt.png]]&lt;br /&gt;
&lt;br /&gt;
= Custom tax per product =&lt;br /&gt;
&lt;br /&gt;
This part will show you how to create a static custom tax per product. Static means that the tax will be stored per product, and not per product&#039;s line, thus you just have to setup the tax once per product and it will always be applied in every invoice you add this product.&lt;br /&gt;
&lt;br /&gt;
To do that, you will need the CustomFields Pro module + ProductsEasyAccess.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
1- Create a product&#039;s custom fields called &amp;quot;custom_tax&amp;quot;. Now, using ProductsEasyAccess, you can access this field wherever you wants, either to show it on the Dolibarr&#039;s interface (see previous chapter), or in a PDF template, which we will describe below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2- Modify your invoice&#039;s PDF template. You first need to load all the products&#039; fields &#039;&#039;before&#039;&#039; the loop over the products&#039; lines. To do this, search for a line like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// This should be placed above the &amp;quot;loop lines&amp;quot; below, add this:&lt;br /&gt;
&lt;br /&gt;
dol_include_once(&#039;/productseasyaccess/lib/productseasyaccess.lib.php&#039;);&lt;br /&gt;
fill_object_with_products_fields($object);&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines, search for this command&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  // Add this just below the for loop, this will ease your php manipulation after.&lt;br /&gt;
  // This will allow you to more easily access the products&#039; fields (this will store the id of each product inside $lineproductid).&lt;br /&gt;
  $line_product_id = $object-&amp;gt;lines[$i]-&amp;gt;fk_product;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4- You can now use your products&#039; fields in your PDF. To access the custom_tax field, just use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And to print it in your PDF:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell(0,3, $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax, 0, &#039;L&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5- Then, if you want to count the custom tax total and add it to your vat total, you can create a counter inside the for loop we cited in the first step:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_custom_tax = 0; // Initialize the custom tax total counter&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  $object-&amp;gt;total_custom_tax += $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then find inside the function **_tableau_tot**, find the following line:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And prepend the total modification like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc + $object-&amp;gt;total_custom_tax; // prepend this to add the custom tax to the total&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Done. This should show your custom tax per product and your total including all custom taxes.&lt;/div&gt;</summary>
		<author><name>Lrq3000</name></author>
	</entry>
	<entry>
		<id>https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33074</id>
		<title>Module CustomFields Cases</title>
		<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33074"/>
		<updated>2015-01-15T17:21:19Z</updated>

		<summary type="html">&lt;p&gt;Lrq3000: /* Refresh the price instantly */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:CustomFields]]&lt;br /&gt;
{{TemplateDocDevEn}}&lt;br /&gt;
{{TemplateModEN}}&lt;br /&gt;
{{BasculeDevUserEn|&lt;br /&gt;
name=CustomFields|&lt;br /&gt;
num=8500|&lt;br /&gt;
devdoc=[[Module_CustomFields_Dev]]|&lt;br /&gt;
userdoc=[[Module_CustomFields]]|}}&lt;br /&gt;
This page lists a compilation of concrete, practical examples uses of [[Module_CustomFields|CustomFields]] and of its various features.&lt;br /&gt;
&lt;br /&gt;
If you had an experience with CustomFields, you can also add here your case as an example for others to get inspired.&lt;br /&gt;
&lt;br /&gt;
= How to make a generic PDF template listing all custom fields =&lt;br /&gt;
&lt;br /&gt;
To make a generic PDF template listing all custom fields, you just need to take an existing PDF template, then you need to follow these 3 steps:&lt;br /&gt;
&lt;br /&gt;
1/ Edit the class name (required, else it will conflict with the original template).&lt;br /&gt;
&lt;br /&gt;
Eg: if we take the crabe template for invoices, we change:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;crabe&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
into:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_customfields extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;customfields&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2/ Copy this generic function at the end of the PDF:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propale/order/etc... (CustomFields simpler functions will automatically adapt)&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs); // set default PDF font size&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
    // Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
    $customfields = customfields_fill_object($object, null, $outputlangs, null, true);&lt;br /&gt;
&lt;br /&gt;
    // Setting the starting position of the text cursor&lt;br /&gt;
    $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
    $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
    // Printing the customfields&lt;br /&gt;
    foreach ($object-&amp;gt;customfields as $label=&amp;gt;$value) { // $value is already formatted!&lt;br /&gt;
        // Get translated label&lt;br /&gt;
        $translatedlabel = $customfields-&amp;gt;findLabelPDF($label, $outputlangs); // translated label of the customfield (not translated by default in customfields_fill_object() because a field should always be accessible by a base name, whatever the translation is)&lt;br /&gt;
&lt;br /&gt;
        // PDF formatting, placement and printing&lt;br /&gt;
        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
        $pdf-&amp;gt;MultiCell(0,3, $translatedlabel.&#039; ($object-&amp;gt;customfields-&amp;gt;&#039;.$label.&#039;): &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternative function (with a bit less functionalities, but more controllable):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page (used to debug if CustomFields setup is correct)&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propal/product/whatever...&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs);&lt;br /&gt;
&lt;br /&gt;
            if (empty($object-&amp;gt;table_element) or empty($object-&amp;gt;id)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;Current \$object is not compatible with CustomFields, could not find table_element or id.&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
    $customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element);&lt;br /&gt;
&lt;br /&gt;
            // Fetching custom fields records&lt;br /&gt;
            $fields = $customfields-&amp;gt;fetch($object-&amp;gt;id);&lt;br /&gt;
&lt;br /&gt;
            if (!isset($fields)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;No custom field could be found for this object. Please check your configuration (did you create at least one customfield and set a value in the current datasheet?)&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            } else {&lt;br /&gt;
                // Setting the starting position of the text cursor&lt;br /&gt;
                $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
                $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
                // Printing the customfields&lt;br /&gt;
                foreach ($fields as $key=&amp;gt;$field) {&lt;br /&gt;
                        $translatedname = $customfields-&amp;gt;findLabelPDF($key, $outputlangs); // label of the customfield&lt;br /&gt;
                        $value = $customfields-&amp;gt;simpleprintFieldPDF($key, $field, $outputlangs); // value (cleaned and properly formatted) of the customfield&lt;br /&gt;
&lt;br /&gt;
                        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
                        $pdf-&amp;gt;MultiCell(0,3, $translatedname.&#039;: &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
                        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3/ Call the _pagecustomfields function at the right place (generally, after _pagefoot and just before $pdf-&amp;gt;Close()):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Pied de page&lt;br /&gt;
$this-&amp;gt;_pagefoot($pdf,$object,$outputlangs);&lt;br /&gt;
$pdf-&amp;gt;AliasNbPages();&lt;br /&gt;
&lt;br /&gt;
// CustomFields&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) { // if the customfields module is activated...&lt;br /&gt;
        // Add a page with the customfields&lt;br /&gt;
        $pdf-&amp;gt;AddPage();&lt;br /&gt;
        $this-&amp;gt;_pagecustomfields($pdf,$object,$outputlangs);&lt;br /&gt;
        $pagenb++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
$pdf-&amp;gt;Close(); // you should first search for this, you should only find one occurrence&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then go to the module&#039;s admin panel (eg: invoice admin panel) and enable the template you&#039;ve just created.&lt;br /&gt;
&lt;br /&gt;
You can now use it to test if your customfields work.&lt;br /&gt;
&lt;br /&gt;
= Conditions on fields selection =&lt;br /&gt;
&lt;br /&gt;
Sometimes you may want to show a DropdownBox or Constraint (or another multi-choice type), but you may want to show or hide choices based on conditions (eg: only if it&#039;s a prospect, only if this zone was selected, only if this category was selected, etc..).&lt;br /&gt;
&lt;br /&gt;
To know which is the best solution you should choose to implement your condition, you should first know in which category your condition falls in.&lt;br /&gt;
&lt;br /&gt;
== Conditions categories ==&lt;br /&gt;
&lt;br /&gt;
Conditions can be categorized into 4 types:&lt;br /&gt;
&lt;br /&gt;
* static condition: choices are restrained on a condition that never changes (eg: only show third-parties that are suppliers, or lowercase all data on sql insertion, or check that a field is above a certain number, etc.).&lt;br /&gt;
* semi-dynamic condition: choices that are constrained on a static table but based on the value of another field/customfield. This is also commonly called &amp;quot;Cascaded dropdown lists&amp;quot; or &amp;quot;Dynamically linked dropdown lists&amp;quot;.&lt;br /&gt;
* dynamic: everything is dynamic: choices are based on a subfield of a field of the current module, so that there&#039;s no material table and it&#039;s impossible to create a view (because you would need to create one view per id) (eg: show only the contacts attached to the third-party being accessed)&lt;br /&gt;
* timed condition: a condition that is based on time or recurrence (eg: at 5:00 AM delete old records, etc..)&lt;br /&gt;
&lt;br /&gt;
== Conditions solutions ==&lt;br /&gt;
&lt;br /&gt;
Now that you know what kind of condition you want to set, you can choose one of the following solutions depending on your preferences and knowledge.&lt;br /&gt;
&lt;br /&gt;
In any case, you can always use the &#039;&#039;&#039;CustomFields&#039;s overload functions&#039;&#039;&#039; to fit your needs for nearly every type of condition.&lt;br /&gt;
&lt;br /&gt;
=== Static condition ===&lt;br /&gt;
&lt;br /&gt;
* View: create a view on the table based on the condition, and then create a custom field constrained on this view (which is just like any table), eg: only show third-parties that are suppliers:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
CREATE VIEW llx_societe_supplier_view AS&lt;br /&gt;
SELECT *&lt;br /&gt;
FROM llx_societe&lt;br /&gt;
WHERE fournisseur=1;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
WARNING: foreign keys (and thus constraints) can&#039;t be used on views with MySQL, and materialized views neither exist, so you can&#039;t use this method with MySQL.&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
* SQL Transformations&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
=== Semi-dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade option is ideal in most cases, and if not enough, you can use the Cascade Custom option and Overloading Functions.&lt;br /&gt;
* View&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
&lt;br /&gt;
=== Dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade Custom and Overload functions&lt;br /&gt;
* Making your own module, which calls the CustomFields class&lt;br /&gt;
&lt;br /&gt;
=== Timed condition ===&lt;br /&gt;
&lt;br /&gt;
* SQL scheduled events&lt;br /&gt;
* Cron job&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
= Overloading functions =&lt;br /&gt;
&lt;br /&gt;
Here are a few concrete use cases of overloading functions to achieve various goals.&lt;br /&gt;
&lt;br /&gt;
== Dynamic list selection based on another one ==&lt;br /&gt;
&lt;br /&gt;
The goal is to show two custom fields on the Third-Party module: one which gives the zone (secteur) of the third-party (Isere, Alpes du sud, Haute-Savoie...) and the other which gives relative to the zone the list of all the ski resorts (station_a) inside the selected zone.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will show how to manually manage this kind of requirements using [[Module_CustomFields#Overloading_functions:_Adding_your_own_management_code_for_custom_fields|overloading functions]], but in latest CustomFields releases, the same solution can be achieved automatically using the [[Module_CustomFields#Cascade|Cascade option]].&lt;br /&gt;
&lt;br /&gt;
We have one table &#039;&#039;&#039;llx_k_station&#039;&#039;&#039; (rowid, station (char50), secteur(char50)) which contains the following: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1,les 2 alpes,isère&lt;br /&gt;
2,chamrousse,isère&lt;br /&gt;
3,alpe d&#039;huez,isère&lt;br /&gt;
4,vars,alpes du sud&lt;br /&gt;
5,risoul,alpes du sud&lt;br /&gt;
etc...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In CustomFields&#039;s admin panel, we create two custom fields:&lt;br /&gt;
&lt;br /&gt;
1- secteur: returns the list of all zones - type DropdownBox: enum(&#039;- Aucun&#039;,&#039;Alpes du Sud&#039;,&#039;export&#039;,&#039;Haute-Savoie&#039;,&#039;Isère&#039;, etc...)&lt;br /&gt;
&lt;br /&gt;
2- station_a: returns the list of all ski resorts - type constraint on &#039;&#039;&#039;llx_k_station&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Here is the code to put in customfields_fields_extend.lib.php that will allow to show only the ski resorts corresponding to the selected zone:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_editview_societe_station_a (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$parameters, &amp;amp;$action, &amp;amp;$user, &amp;amp;$idvar, &amp;amp;$rightok, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
  global $db; // allow to access database functions&lt;br /&gt;
  $sql=&amp;quot;SELECT llx_k_station.station, llx_k_station.rowid FROM llx_societe_customfields INNER JOIN llx_k_station ON llx_societe_customfields.Secteur = llx_k_station.secteur WHERE llx_societe_customfields.fk_societe=&amp;quot;.$object-&amp;gt;id;&lt;br /&gt;
$result=$db-&amp;gt;query($sql);&lt;br /&gt;
 &lt;br /&gt;
  if ($result) {&lt;br /&gt;
    $num = $db-&amp;gt;num_rows($result);&lt;br /&gt;
    $i = 0;&lt;br /&gt;
    $myarray = array();&lt;br /&gt;
    while ($i &amp;lt; $num)&lt;br /&gt;
    {&lt;br /&gt;
      $obj = $db-&amp;gt;fetch_object($result);&lt;br /&gt;
      $myarray []=array(&#039;id&#039;=&amp;gt;$obj-&amp;gt;rowid, &#039;value&#039;=&amp;gt;$obj-&amp;gt;station);&lt;br /&gt;
      $i++;&lt;br /&gt;
    }&lt;br /&gt;
    $value = $myarray; &lt;br /&gt;
    $db-&amp;gt;free($result);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to manub for giving this case.&lt;br /&gt;
&lt;br /&gt;
== Dynamic discount computation per products lines on propales ==&lt;br /&gt;
&lt;br /&gt;
The goal here is to compute the discount for a product line in propales based on a computation on three custom fields.&lt;br /&gt;
&lt;br /&gt;
First, create three custom fields for &amp;quot;Line commercial proposals&amp;quot;: vl, cpl and lc.&lt;br /&gt;
&lt;br /&gt;
Then add the following overloading function at the end of your customfields_fields_extend.lib.php file (read the comments for more informations):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_propaldet_vl (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
&lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $vl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;vl&#039;};&lt;br /&gt;
    $cpl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;cpl&#039;};&lt;br /&gt;
    $lc = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;lc&#039;};&lt;br /&gt;
&lt;br /&gt;
    // Compute the discount&lt;br /&gt;
    $remise_calculee = (100 - $vl) * (100 - $cpl) * (100 - $lc);&lt;br /&gt;
&lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $remise_calculee = price2num($remise_calculee);&lt;br /&gt;
&lt;br /&gt;
    // Assign the new discount into the PropalLine object&lt;br /&gt;
    $object-&amp;gt;remise_percent = $remise_calculee;&lt;br /&gt;
&lt;br /&gt;
    // Update the price (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
&lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
&lt;br /&gt;
    //$q = &amp;quot;UPDATE  &amp;quot;.MAIN_DB_PREFIX.&amp;quot;propaldet SET remise_percent=&amp;quot;.$remise_calculee.&amp;quot; WHERE  rowid=&amp;quot;.$object-&amp;gt;rowid;&lt;br /&gt;
    //$db-&amp;gt;query($q);&lt;br /&gt;
    &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;vl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;cpl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;lc&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Linking Dolibarr objects from two different modules =&lt;br /&gt;
&lt;br /&gt;
Dolibarr sometimes offers the possibility to link two different modules. Eg: when you convert an Intervention card into an Invoice.&lt;br /&gt;
&lt;br /&gt;
In this case, you might want in your templates to fetch the custom fields of the linked object. Eg: you generate a PDF from the Invoice, but you want to get the custom fields from the Intervention that was converted into the Invoice.&lt;br /&gt;
&lt;br /&gt;
The key here is to use the &#039;&#039;&#039;llx_element_element&#039;&#039;&#039; table or the &#039;&#039;&#039;llx_element_*&#039;&#039;&#039; tables (eg: llx_element_contact): this is a standard Dolibarr table that stores all links between objects of two different modules.&lt;br /&gt;
&lt;br /&gt;
Here is the structure:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
rowid | fk_source | sourcetype | fk_target | targettype&lt;br /&gt;
-------------------------------------------------------&lt;br /&gt;
1 | 2 | fichinter | 5 | facture&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can then use a php code like the following to populate your $object with customfields:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
// This will also create and returns us an instance of the CustomFields class, which we will use to manually query the llx_element_element table&lt;br /&gt;
$customfields = customfields_fill_object($object, null, $outputlangs, &#039;facture&#039;, true); // beautified values&lt;br /&gt;
&lt;br /&gt;
// Querying the llx_element_element table manually to get the link between the invoice and the linked intervention card (we want the id of the intervention card)&lt;br /&gt;
// fetchAny($columns, $table, $where=&#039;&#039;, $orderby=&#039;&#039;, $limitby=&#039;&#039;)&lt;br /&gt;
$eltrow = $customfields-&amp;gt;fetchAny(&#039;*&#039;, MAIN_DB_PREFIX.&#039;element_element&#039;, &#039;targettype=&amp;quot;&#039;.$object-&amp;gt;element.&#039;&amp;quot; and fk_target=&#039;.$object-&amp;gt;id.&#039; and sourcetype=&amp;quot;fichinter&amp;quot;&#039;);&lt;br /&gt;
&lt;br /&gt;
// Save the intervention&#039;s id&lt;br /&gt;
$sourceid = $eltrow[0][&#039;fk_source&#039;];&lt;br /&gt;
&lt;br /&gt;
// Crafting a dummy intervention object&lt;br /&gt;
$fromobject = new stdClass(); // Init an empty object&lt;br /&gt;
$fromobject-&amp;gt;id = $sourceid; // We need the id and..&lt;br /&gt;
$fromobject-&amp;gt;table_element = &#039;fichinter&#039;; // the table_element (name of the table)&lt;br /&gt;
&lt;br /&gt;
// We can then fill $object with the intervention custom fields (we store them in a sub property &#039;fichinter&#039; to avoid conflicts with invoice&#039;s custom fields)&lt;br /&gt;
customfields_fill_object($object, $fromobject, $outputlangs, &#039;fichinter&#039;, true);&lt;br /&gt;
&lt;br /&gt;
// Now you can access invoices custom fields using:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;facture-&amp;gt;cf_myfield);&lt;br /&gt;
// And access intervention custom fields with:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;fichinter-&amp;gt;cf_myfield);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to netassopro for the tip.&lt;br /&gt;
&lt;br /&gt;
= Modify the total global price with a coefficient (without tampering the discount) =&lt;br /&gt;
&lt;br /&gt;
This is more troublesome than modifying the total price per product/service line, because the total global price of an object (let&#039;s say an invoice, but this also works for any other object) is always recomputed by summing the prices and quantity of each product/service line. This means that although there&#039;s an entry &amp;quot;total_ttc&amp;quot; in the database to store the invoice&#039;s total price, this price is in fact always recomputed, it&#039;s never used as-is. Thus, it&#039;s useless to change only the database record: we need to inject some code to change how the total price is computed.&lt;br /&gt;
&lt;br /&gt;
This could be done using an overloading function or a trigger, but unluckily, Dolibarr doesn&#039;t call a trigger nor a hook when recomputing the price. We thus need to edit core files to do what we want.&lt;br /&gt;
&lt;br /&gt;
However, the bright side is that we can cleverly edit just two functions from one core file: update_price() and getMarginInfos() from /htdocs/core/class/commonobject.class.php , and everything should run smoothly. Beware that if you update Dolibarr, you will need to redo these two modifications in the code.&lt;br /&gt;
&lt;br /&gt;
First, you need to create a new custom field called &amp;quot;global_coefficient&amp;quot; in Invoices (not Invoices Lines!). Then you can continue to the subchapters below.&lt;br /&gt;
&lt;br /&gt;
Also note that this global coefficient is also compatible with per line coefficients (as described in the next chapter), thus you can use a global coefficient and per line coefficients at the same time. You can also use multiple global coefficient if you wish.&lt;br /&gt;
&lt;br /&gt;
Another note: if you have a grid of discounts depending on quantity or price or whatever parameter, you can program a precomputation code using a Custom Ajax function (this way, your discounts will be automatically computed, but then you can still manually enter them for particular cases since the custom field is editable).&lt;br /&gt;
&lt;br /&gt;
== Implement the change of global price in most of Dolibarr (including remaining debt to pay and PDF and ODT) ==&lt;br /&gt;
&lt;br /&gt;
The change we will implement here will reflect the new total price with the global coefficient in most of Dolibarr&#039;s code, since we will here change the main function that is usually called to update and get the total price.&lt;br /&gt;
&lt;br /&gt;
Open /htdocs/core/class/commonobject.class.php and look for the function update_price().&lt;br /&gt;
&lt;br /&gt;
You should see the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function update_price($exclspec=0,$roundingadjust=&#039;none&#039;,$nodatabaseupdate=0,$seller=&#039;&#039;)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just below the include_once line, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;;&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/conf/conf_customfields_func.lib.php&#039;;&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $this-&amp;gt;table_element);&lt;br /&gt;
// Get object&#039;s id (either stored in property &amp;quot;id&amp;quot; or &amp;quot;rowid&amp;quot;)&lt;br /&gt;
$this_id = (!empty($this-&amp;gt;id)) ? $this-&amp;gt;id : $this-&amp;gt;rowid;&lt;br /&gt;
// Load custom fields records&lt;br /&gt;
$cf_parent_record = $customfields-&amp;gt;fetch($this_id);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will load the global_coefficient custom field (and also any other custom field you have defined for invoices).&lt;br /&gt;
&lt;br /&gt;
Now scroll below, and you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
while ($i &amp;lt; $num)&lt;br /&gt;
{&lt;br /&gt;
    $obj = $this-&amp;gt;db-&amp;gt;fetch_object($resql);&lt;br /&gt;
&lt;br /&gt;
    ... (lots of code)&lt;br /&gt;
&lt;br /&gt;
    i++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Add revenue stamp to total&lt;br /&gt;
$this-&amp;gt;total_ttc       += isset($this-&amp;gt;revenuestamp)?$this-&amp;gt;revenuestamp:0;&lt;br /&gt;
&lt;br /&gt;
$this-&amp;gt;db-&amp;gt;free($resql);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is the end of the loop that is summing each product/service line price to compute the total price. Just after this loop, you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Now update global field total_ht, total_ttc and tva&lt;br /&gt;
$fieldht=&#039;total_ht&#039;;&lt;br /&gt;
$fieldtva=&#039;tva&#039;;&lt;br /&gt;
$fieldlocaltax1=&#039;localtax1&#039;;&lt;br /&gt;
$fieldlocaltax2=&#039;localtax2&#039;;&lt;br /&gt;
$fieldttc=&#039;total_ttc&#039;;&lt;br /&gt;
// Specific code for backward compatibility with old field names&lt;br /&gt;
if ($this-&amp;gt;element == &#039;facture&#039; || $this-&amp;gt;element == &#039;facturerec&#039;)             $fieldht=&#039;total&#039;;&lt;br /&gt;
if ($this-&amp;gt;element == &#039;facture_fourn&#039; || $this-&amp;gt;element == &#039;invoice_supplier&#039;) $fieldtva=&#039;total_tva&#039;;&lt;br /&gt;
if ($this-&amp;gt;element == &#039;propal&#039;)                                                $fieldttc=&#039;total&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Between those two blocks of code (so just above // Now update global field total_ht, total_ttc and tva), you can place the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
if (!empty($cf_parent_record-&amp;gt;global_coefficient)) { // avoid applying the coefficient if empty (this will return a 0 price)&lt;br /&gt;
    $this-&amp;gt;total_ht = $this-&amp;gt;total_ht * $cf_parent_record-&amp;gt;global_coefficient;&lt;br /&gt;
    $this-&amp;gt;total_tva = $this-&amp;gt;total_tva * $cf_parent_record-&amp;gt;global_coefficient;&lt;br /&gt;
    $this-&amp;gt;total_ttc = $this-&amp;gt;total_ttc * $cf_parent_record-&amp;gt;global_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Done, now try to add/edit a product/service line, and the total price should be affected by the global coefficient.&lt;br /&gt;
&lt;br /&gt;
== Implementing the change of price in margin module ==&lt;br /&gt;
&lt;br /&gt;
If you enabled the margin module, you should see that the coefficient is not applied there, but it&#039;s easy to fix that.&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/class/commonobject.class.php and find the function getMarginInfos(), then inside find the following lines:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach($this-&amp;gt;lines as $line) {&lt;br /&gt;
    if (empty($line-&amp;gt;pa_ht) &amp;amp;&amp;amp; isset($line-&amp;gt;fk_fournprice) &amp;amp;&amp;amp; !$force_price) {&lt;br /&gt;
        $product = new ProductFournisseur($this-&amp;gt;db);&lt;br /&gt;
        if ($product-&amp;gt;fetch_product_fournisseur_price($line-&amp;gt;fk_fournprice))&lt;br /&gt;
            $line-&amp;gt;pa_ht = $product-&amp;gt;fourn_unitprice * (1 - $product-&amp;gt;fourn_remise_percent / 100);&lt;br /&gt;
        if (isset($conf-&amp;gt;global-&amp;gt;MARGIN_TYPE) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;MARGIN_TYPE == &amp;quot;2&amp;quot; &amp;amp;&amp;amp; $product-&amp;gt;fourn_unitcharges &amp;gt; 0)&lt;br /&gt;
            $line-&amp;gt;pa_ht += $product-&amp;gt;fourn_unitcharges;&lt;br /&gt;
    }&lt;br /&gt;
    // si prix d&#039;achat non renseigné et devrait l&#039;être, alors prix achat = prix vente&lt;br /&gt;
    if ((!isset($line-&amp;gt;pa_ht) || $line-&amp;gt;pa_ht == 0) &amp;amp;&amp;amp; $line-&amp;gt;subprice &amp;gt; 0 &amp;amp;&amp;amp; (isset($conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull == 1)) {&lt;br /&gt;
        $line-&amp;gt;pa_ht = $line-&amp;gt;subprice * (1 - ($line-&amp;gt;remise_percent / 100));&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the lines are filled with margin infos. The price has just been fetched here, and we will now modify it with the coefficient before the price gets used for margin computation.&lt;br /&gt;
&lt;br /&gt;
To optimize things up, we will load the custom fields before the loop (because they don&#039;t change between product/service lines, they are the custom fields of the parent object), and then apply the global_coefficient inside the loop.&lt;br /&gt;
&lt;br /&gt;
Just above the code we found (just before the for loop), paste the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
// Get object&#039;s id (either stored in property &amp;quot;id&amp;quot; or &amp;quot;rowid&amp;quot;)&lt;br /&gt;
$this_id = (!empty($this-&amp;gt;id)) ? $this-&amp;gt;id : $this-&amp;gt;rowid;&lt;br /&gt;
// Load custom fields records&lt;br /&gt;
$customfields = customfields_fill_object($this);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And now, just below the code we found, inside the loop (and just above the comment: &amp;quot;// calcul des marges&amp;quot;), paste the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
// Apply the coefficient&lt;br /&gt;
if (!empty($this-&amp;gt;customfields-&amp;gt;cf_global_coefficient)) {&lt;br /&gt;
    $line-&amp;gt;pa_ht = $line-&amp;gt;pa_ht * $this-&amp;gt;customfields-&amp;gt;cf_global_coefficient;&lt;br /&gt;
    $line-&amp;gt;subprice = $line-&amp;gt;subprice * $this-&amp;gt;customfields-&amp;gt;cf_global_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that here we cheated a bit: we do not compute the coefficient on the global total price, but rather per product/service line price. This is done on purpose, because mathematically this should produce no difference, and it also allows Dolibarr to continue peacefully with its margin calculations (else we would have to adapt all those computations, which would imply a lot more of code changes!).&lt;br /&gt;
&lt;br /&gt;
That&#039;s all, the price should be reflected in the margin. Just refresh the page to reflect the change.&lt;br /&gt;
&lt;br /&gt;
== Refresh the price instantly ==&lt;br /&gt;
&lt;br /&gt;
Maybe you noticed one glitch: when you modify the global_coefficient, the global total price isn&#039;t updated until you add/edit a product/service line. This is because the update_price() function is only called when we add/edit a product/service line.&lt;br /&gt;
&lt;br /&gt;
To fix that, we need now to force the refreshing of price. To do that, we will use an &amp;quot;aftersave&amp;quot; overloading function, which will execute commands after our custom field is saved.&lt;br /&gt;
&lt;br /&gt;
Go to the folder /htdocs/customfields/fields and rename the file customfields_fields_extend.lib.php.example into customfields_fields_extend.lib.php .&lt;br /&gt;
&lt;br /&gt;
Now, open it in your favourite text editor, and add the following function :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_aftersave_facture_global_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value, $fields) {&lt;br /&gt;
    // Force refreshing total price&lt;br /&gt;
    $object-&amp;gt;parent_object-&amp;gt;update_price(0, &#039;auto&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s all. Now, when you edit your global_coefficient, this will force refresh the total price.&lt;br /&gt;
&lt;br /&gt;
However, you will still notice one last glitch: when you edit the global_coefficient, the total price isn&#039;t reflected instantly, you must refresh the webpage once to see the new price. This is because of how the datasheet and the CustomFields hook are designed: CustomFields is called only exactly where the custom fields are place, thus it is called after the total price, the margin and the remaining debt are printed. Thus, CustomFields updates the value of your custom field only after all those informations get printed, and thus it cannot modify them at this stage. That&#039;s why those information are out-of-date, and you must refresh the webpage once to get to see the latest values.&lt;br /&gt;
&lt;br /&gt;
There is nothing you can do about it. One way to fix that would be to modify how CustomFields save the custom fields, by moving this stage at the very top. However, this would need another hook, which should be called at the very beginning of the page loading, which should be made only for this purpose (PageLoading), and should be available in all modules supported by CustomFields in order to not break anything.&lt;br /&gt;
&lt;br /&gt;
= Modify the total price PER product/service line with a coefficient (without tampering the discount) =&lt;br /&gt;
&lt;br /&gt;
Here we will go through the creation and management of a custom field called &#039;&#039;&#039;coefficient&#039;&#039;&#039; that will change the total price of each product by modifying it by the coefficient.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will not create a coefficient per product but rather per product&#039;s line. Thus if you want to create a coefficient that never change between invoices but only depending on the product and stays static, please refer to the next chapter below about creating a custom tax per product.&lt;br /&gt;
&lt;br /&gt;
The preliminary and necessary step is to first create a custom field that will be used to modify the total price. We will create one for Client Invoice Lines.&lt;br /&gt;
&lt;br /&gt;
Go to the CustomFields administration panel, then into the Client Invoice Lines tab, then click on the button New Field, name the field &#039;&#039;&#039;coefficient&#039;&#039;&#039;, and set the type to &#039;&#039;&#039;Double&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
When you go to client invoices in Dolibarr, you should then see something similar to this:&lt;br /&gt;
[[File:Cfe1.jpg]]&lt;br /&gt;
You can see that our coefficient field was successfully created.&lt;br /&gt;
&lt;br /&gt;
Then here are two alternative ways of implementing the change of the total price of each product relativery to this coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change total price in database ==&lt;br /&gt;
&lt;br /&gt;
Here we will use a &amp;quot;save&amp;quot; overloading function to change the total price directly in the database, thus we won&#039;t have to modify anything else (eg: nor PDF templating nor ODT) since the total price will be already updated in the database.&lt;br /&gt;
&lt;br /&gt;
Warning: this method will work only with CustomFields =&amp;gt; 3.2.23 since the &amp;quot;save&amp;quot; overloading function did not work for products lines before.&lt;br /&gt;
&lt;br /&gt;
Just add the following overloading function in your customfields_fields_extend.lib.php file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_facturedet_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
 &lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $coefficient = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;coefficient&#039;}; // you could also use $coefficient = $value;&lt;br /&gt;
 &lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $coefficient = price2num($coefficient);&lt;br /&gt;
 &lt;br /&gt;
    // Get the original subprices (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
 &lt;br /&gt;
    // Update the subprices with the coefficient&lt;br /&gt;
    $object-&amp;gt;total_ht = $object-&amp;gt;total_ht * $coefficient; // compute no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva = $object-&amp;gt;total_tva * $coefficient; // compute tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc * $coefficient; // compute tax included price&lt;br /&gt;
 &lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(&#039;&#039;,true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
 &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;coefficient&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should show the correct total price on the Dolibarr&#039;s interface without modifying the unit subprice (P.U. Unit):&lt;br /&gt;
&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
You have nothing to edit in your PDF or ODT template, since the new subprice with applied coefficient will be automatically printed instead of the old one without coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change printed total price per product without tampering the subprice in the database ==&lt;br /&gt;
&lt;br /&gt;
Here is a way to change the printed total price (the one shown on screen and in PDF/ODT) but without changing the total price in the database.&lt;br /&gt;
&lt;br /&gt;
The following sub-chapters are all independent, thus you can only follow the ones that you require, eg: if you only need to change the price in your PDF documents, just create a custom field then jump to the sub-chapter to &#039;&#039;&#039;Implement the change of price in PDF&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in ODT templates ===&lt;br /&gt;
&lt;br /&gt;
There&#039;s no eval implementation currently inside ODT templates, but there is a kind of hook implementation, called [[Create_an_ODT_document_template#Other_personalized_tags|substitution functions]].&lt;br /&gt;
&lt;br /&gt;
You can either create your own module, then create your own substitution function, but for the sake of simplicity in this tutorial, we will directly use the CustomFields substitution function. However, please remember that you will have to redo your changes everytime you will update the CustomFields module, so if you don&#039;t want to do that it would be better if you create your own module and substitution function.&lt;br /&gt;
&lt;br /&gt;
Open the file &#039;&#039;&#039;htdocs/customfields/core/substitutions/functions_customfields.lib.php&#039;&#039;&#039; and go to the function &#039;&#039;&#039;customfields_completesubstitutionarray_lines&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
At the very end of this function, but inside the customfields check:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) {&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
But just before the ending bracket, you should add the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($object-&amp;gt;table_element_line === &#039;facturedet&#039;) { // First check that we are in the right module, we don&#039;t want to use coefficient for other modules if we didn&#039;t define the custom field there&lt;br /&gt;
    $substitutionarray[&#039;line_price_full&#039;] = $substitutionarray[&#039;line_price_ttc&#039;] * $substitutionarray[&#039;cf_coefficient&#039;]; // multiply the total price with our coefficient&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code will add a new tag &#039;&#039;&#039;{line_price_full}&#039;&#039;&#039; that you can then use inside your ODT templates.&lt;br /&gt;
&lt;br /&gt;
Just place this tag in a table in your ODT document, between the table tags ( [!-- BEGIN row.lines --] ... [!-- END row.lines --] ), and this will print your full price.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in PDF templates ===&lt;br /&gt;
&lt;br /&gt;
First, we will create our own PDF template by copying an existing template, and then we will edit it to change the price of items.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Creating your own PDF template by copying an existing template&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Go into the following folder: &#039;&#039;&#039;htdocs/core/modules/facture/doc/&#039;&#039;&#039;, then copy the file &#039;&#039;&#039;pdf_crabe.modules.php&#039;&#039;&#039; and name the new one &#039;&#039;&#039;pdf_crabecoeff.modules.php&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Then open it in your favourite editor, and change the following lines:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures // change pdf_crabe into pdf_crabecoeff&lt;br /&gt;
{&lt;br /&gt;
...&lt;br /&gt;
    function __construct($db)&lt;br /&gt;
    {&lt;br /&gt;
        ...&lt;br /&gt;
	$this-&amp;gt;name = &amp;quot;crabe&amp;quot;; // change &amp;quot;crabe&amp;quot; into &amp;quot;crabecoeff&amp;quot;&lt;br /&gt;
        ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you have your own PDF template!&lt;br /&gt;
&lt;br /&gt;
Now you should &#039;&#039;&#039;enable your PDF template&#039;&#039;&#039; before doing anything else, by going into the Modules Administration panel, and into the Invoice admin page, then click on the ON button at the right of the &#039;&#039;&#039;crabecoeff&#039;&#039;&#039; entry to enable the template.&lt;br /&gt;
&lt;br /&gt;
Now, let&#039;s proceed onto modifying the prices.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Changing the prices automatically in your PDF&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
You can now do pretty much anything you want in your PDF template, but I will give you a mean to automatically change the prices of every products with our coefficient, as well as the total price, without editing too much the template.&lt;br /&gt;
&lt;br /&gt;
First, try to find the following code of line (should be inside the write_file() method):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf=pdf_getInstance($this-&amp;gt;format);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, just copy/paste the following code &#039;&#039;&#039;below&#039;&#039;&#039; pdf_getInstance():&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$this-&amp;gt;customfields = customfields_fill_object($object, null, $outputlangs, null, true); // beautified values&lt;br /&gt;
$this-&amp;gt;customfields_raw = customfields_fill_object($object, null, $outputlangs, &#039;raw&#039;, null); // raw values&lt;br /&gt;
$this-&amp;gt;customfields_lines = customfields_fill_object_lines($object, null, $outputlangs, null, true); // product lines&#039; values&lt;br /&gt;
&lt;br /&gt;
// Resetting every $object price because we will compute them anew&lt;br /&gt;
$object-&amp;gt;total_ht = 0;&lt;br /&gt;
$object-&amp;gt;total_tva = 0;&lt;br /&gt;
$object-&amp;gt;total_ttc = 0;&lt;br /&gt;
// For each line of product/service, we will recompute all the prices&lt;br /&gt;
foreach($object-&amp;gt;lines as $line) {&lt;br /&gt;
    // Get the coefficient custom field&#039;s value for the current line&lt;br /&gt;
    $coeff = $object-&amp;gt;customfields-&amp;gt;lines-&amp;gt;{$line-&amp;gt;rowid}-&amp;gt;cf_coefficient;&lt;br /&gt;
    // Recompute the price of this product/service line&lt;br /&gt;
    $line-&amp;gt;total_ht = $line-&amp;gt;total_ht*$coeff; // compute no tax price&lt;br /&gt;
    $line-&amp;gt;total_tva = $line-&amp;gt;total_tva*$coeff; // compute tax amount&lt;br /&gt;
    $line-&amp;gt;total_ttc = $line-&amp;gt;total_ttc*$coeff; // compute tax included price&lt;br /&gt;
    // Do the sum of all new products/services prices and tva (will be incremented with every product/service line)&lt;br /&gt;
    $object-&amp;gt;total_ht += $line-&amp;gt;total_ht; // global no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva += $line-&amp;gt;total_tva; // global vat/tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc += $line-&amp;gt;total_ttc; // global  tax included price&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you should try to generate your PDF document, and you should get something like this:&lt;br /&gt;
[[File:Cfe3.jpg]]&lt;br /&gt;
&lt;br /&gt;
Note: printing the coefficient as a column in your PDF document is also possible but will not be covered here because PDF templates will vary for every persons depending on your needs.&lt;br /&gt;
For guidelines about how to do this, you can read the documentation on [[Create_a_PDF_document_template|how to create a PDF template]] and [[Module_CustomFields#Implementing_in_PDF_templates|how to use custom fields in PDF templates]].&lt;br /&gt;
&lt;br /&gt;
Hint: you should be looking inside the following conditional block, inside the write_file() method:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
{&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of total price in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
Unluckily there is not (yet) any hooking context to modify how lines of products/services are printed, thus the only thing you can do is directly edit the &#039;&#039;&#039;template file&#039;&#039;&#039; and manually put inside your code to load your custom field and print it the way you want using HTML formatting, for our example:&lt;br /&gt;
&lt;br /&gt;
* For Dolibarr 3.3 and above, edit the file &#039;&#039;&#039;htdocs/core/tpl/objectline_view.tpl.php&#039;&#039;&#039;&lt;br /&gt;
* For Dolibarr 3.1 and 3.2, edit the file &#039;&#039;&#039;htdocs/core/tpl/freeproductline_view.tpl.php&#039;&#039;&#039; AND &#039;&#039;&#039;predefinedproductline_view.tpl.php&#039;&#039;&#039; (apply the same changes to both files)&lt;br /&gt;
&lt;br /&gt;
In the template file you opened, find the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And replace it by:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $object-&amp;gt;table_element_line; // you can use $object-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
&lt;br /&gt;
// Modifying the HT price and printing it on the Dolibarr&#039;s datasheet interface&lt;br /&gt;
// Applying a multiplying/dividing coefficient to an HT price will give a mathematically correct TTC price since the taxes are also a multiplier&lt;br /&gt;
// However, if you want to apply other mathematical operations (like addition or substaction), you should use the total price with $line-&amp;gt;total_ttc&lt;br /&gt;
if (!empty($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
    echo price($line-&amp;gt;total_ht * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient);&lt;br /&gt;
} else {&lt;br /&gt;
    echo price($line-&amp;gt;total_ht);&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should produce something like this:&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Technical note: here we are using customfields_fill_object() instead of customfields_fill_object_lines() (the latter being specifically adapted to lines and thus should theoretically be used here) for performance reasons because the template file we are editing is called in a loop, and customfields_fill_object_lines() should be called outside a loop (because it already does the job that is done by the loop), so here we &#039;tricks&#039; the customfields_fill_object() function to avoid this loop by submitting &#039;facturedet&#039; as a module by itself (when in fact it&#039;s a product lines table, but it works the same way nevertheless). This is a good demonstration of the flexibility of the CustomFields functions and methods.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the coefficient column in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
It&#039;s also possible to show the coefficient in a new column on the Dolibarr interface.&lt;br /&gt;
&lt;br /&gt;
To do that, you need to do two things:&lt;br /&gt;
&lt;br /&gt;
1- Add the column title &amp;quot;Coeff.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
2- Add the generator that will input the coefficient value for each line.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Add the column title &amp;quot;Coeff.&amp;quot;&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
Open in your favourite editor the file /htdocs/core/class/commonobject.class.php and inside the function printObjectLines (note the plural s), find the following lines of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Total HT&lt;br /&gt;
print &#039;&amp;lt;td align=&amp;quot;right&amp;quot; width=&amp;quot;50&amp;quot;&amp;gt;&#039;.$langs-&amp;gt;trans(&#039;TotalHTShort&#039;).&#039;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is what prints the Total HT price column. We will place the Coeff column just before.&lt;br /&gt;
&lt;br /&gt;
So, just above this code, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
dol_include_once(&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element_line);&lt;br /&gt;
$coeff_field = $customfields-&amp;gt;fetchFieldStruct(&#039;coefficient&#039;);&lt;br /&gt;
if (!empty($coeff_field)) {&lt;br /&gt;
    print &#039;&amp;lt;td align=&amp;quot;right&amp;quot; width=&amp;quot;50&amp;quot;&amp;gt;&#039;.$langs-&amp;gt;trans(&#039;Coeff.&#039;).&#039;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will take care of showing the Coeff. column, but only for modules where you created such a custom field (because the file we modified is a common object for all modules in Dolibarr, not just invoices, thus this modification will gracefully skip this custom field if it does not exists for other modules).&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Add the generator&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/tpl/objectline_view.tpl.php and try to find the following block of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php if ($line-&amp;gt;special_code == 3)	{ ?&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo $langs-&amp;gt;trans(&#039;Option&#039;); ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } else { ?&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is what prints the total HT price value for each line.&lt;br /&gt;
&lt;br /&gt;
Just above of this block of code, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $object-&amp;gt;table_element_line; // you can use $this-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
if (isset($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
    &amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient; ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just like for the column title, this code will take care of generating the coefficient value only if the custom field is defined, because this file is also a generic template for all modules of Dolibarr.&lt;br /&gt;
&lt;br /&gt;
At this point, everything is done, you should see the following:&lt;br /&gt;
[[File:Cf_wiki_coeff_column.png]]&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in margin module ===&lt;br /&gt;
&lt;br /&gt;
If you enabled the margin module, you should see that the coefficient is not applied there, but it&#039;s easy to fix that.&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/class/commonobject.class.php and find the function getMarginInfos(), then inside find the following lines:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach($this-&amp;gt;lines as $line) {&lt;br /&gt;
    if (empty($line-&amp;gt;pa_ht) &amp;amp;&amp;amp; isset($line-&amp;gt;fk_fournprice) &amp;amp;&amp;amp; !$force_price) {&lt;br /&gt;
        $product = new ProductFournisseur($this-&amp;gt;db);&lt;br /&gt;
        if ($product-&amp;gt;fetch_product_fournisseur_price($line-&amp;gt;fk_fournprice))&lt;br /&gt;
            $line-&amp;gt;pa_ht = $product-&amp;gt;fourn_unitprice * (1 - $product-&amp;gt;fourn_remise_percent / 100);&lt;br /&gt;
        if (isset($conf-&amp;gt;global-&amp;gt;MARGIN_TYPE) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;MARGIN_TYPE == &amp;quot;2&amp;quot; &amp;amp;&amp;amp; $product-&amp;gt;fourn_unitcharges &amp;gt; 0)&lt;br /&gt;
            $line-&amp;gt;pa_ht += $product-&amp;gt;fourn_unitcharges;&lt;br /&gt;
    }&lt;br /&gt;
    // si prix d&#039;achat non renseigné et devrait l&#039;être, alors prix achat = prix vente&lt;br /&gt;
    if ((!isset($line-&amp;gt;pa_ht) || $line-&amp;gt;pa_ht == 0) &amp;amp;&amp;amp; $line-&amp;gt;subprice &amp;gt; 0 &amp;amp;&amp;amp; (isset($conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull == 1)) {&lt;br /&gt;
        $line-&amp;gt;pa_ht = $line-&amp;gt;subprice * (1 - ($line-&amp;gt;remise_percent / 100));&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the lines are filled with margin infos. The price has just been fetched here, and we will now modify it with the coefficient before the price gets used for margin computation.&lt;br /&gt;
&lt;br /&gt;
Just below the code we found, paste the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $line-&amp;gt;table_element;&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
if (!empty($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
    $line-&amp;gt;pa_ht = $line-&amp;gt;pa_ht * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient;&lt;br /&gt;
    $line-&amp;gt;subprice = $line-&amp;gt;subprice * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s all, the price should be reflected in the margin.&lt;br /&gt;
[[File:Cf_wiki_coeff_margin.png]]&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in remaining debt to pay ===&lt;br /&gt;
&lt;br /&gt;
Open /htdocs/core/class/commonobject.class.php and look for the function update_price().&lt;br /&gt;
&lt;br /&gt;
You should see the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function update_price($exclspec=0,$roundingadjust=&#039;none&#039;,$nodatabaseupdate=0,$seller=&#039;&#039;)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just below the include_once line, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;;&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/conf/conf_customfields_func.lib.php&#039;;&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $this-&amp;gt;table_element_line);&lt;br /&gt;
// Fetching line ids from parent object id&lt;br /&gt;
$line_ids = $customfields-&amp;gt;fetchAny(&#039;rowid&#039;, MAIN_DB_PREFIX.$this-&amp;gt;table_element_line, $this-&amp;gt;fk_element.&#039; = &#039;.$this-&amp;gt;id);&lt;br /&gt;
$line_ids = array_values_recursive(&#039;rowid&#039;, $line_ids);&lt;br /&gt;
// Load custom fields&lt;br /&gt;
$cf_records = $customfields-&amp;gt;fetch($line_ids);&lt;br /&gt;
// Reassociate the records with the line id (instead of the customfields record id)&lt;br /&gt;
$cf_line_cfrowid_to_rowid = array_reassociate(&#039;rowid&#039;, &#039;fk_&#039;.$this-&amp;gt;table_element_line, $cf_records);&lt;br /&gt;
$tmp = $cf_records;&lt;br /&gt;
$cf_records = array();&lt;br /&gt;
foreach ($tmp as $k=&amp;gt;$v) { $cf_records[$cf_line_cfrowid_to_rowid[$k]] = $tmp[$k]; }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will load the coefficient custom field (and also any other custom field you have defined for lines).&lt;br /&gt;
&lt;br /&gt;
Now scroll below, and you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
while ($i &amp;lt; $num)&lt;br /&gt;
{&lt;br /&gt;
    $obj = $this-&amp;gt;db-&amp;gt;fetch_object($resql);&lt;br /&gt;
&lt;br /&gt;
    // Note: There is no check on detail line and no check on total, if $forcedroundingmode = &#039;none&#039;&lt;br /&gt;
&lt;br /&gt;
    if ($forcedroundingmode == &#039;0&#039;)	// Check if data on line are consistent. This may solve lines that were not consistent because set with $forcedroundingmode=&#039;auto&#039;&lt;br /&gt;
    {&lt;br /&gt;
        $localtax_array=array($obj-&amp;gt;localtax1_type,$obj-&amp;gt;localtax1_tx,$obj-&amp;gt;localtax2_type,$obj-&amp;gt;localtax2_tx);&lt;br /&gt;
        $tmpcal=calcul_price_total($obj-&amp;gt;qty, $obj-&amp;gt;up, $obj-&amp;gt;remise_percent, $obj-&amp;gt;vatrate, $obj-&amp;gt;localtax1_tx, $obj-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $obj-&amp;gt;info_bits, $obj-&amp;gt;product_type, $seller, $localtax_array);&lt;br /&gt;
        $diff=price2num($tmpcal[1] - $obj-&amp;gt;total_tva, &#039;MT&#039;, 1);&lt;br /&gt;
        if ($diff)&lt;br /&gt;
        {&lt;br /&gt;
            $sqlfix=&amp;quot;UPDATE &amp;quot;.MAIN_DB_PREFIX.$this-&amp;gt;table_element_line.&amp;quot; SET &amp;quot;.$fieldtva.&amp;quot; = &amp;quot;.$tmpcal[1].&amp;quot;, total_ttc = &amp;quot;.$tmpcal[2].&amp;quot; WHERE rowid = &amp;quot;.$obj-&amp;gt;rowid;&lt;br /&gt;
            dol_syslog(&#039;We found unconsistent data into detailed line (difference of &#039;.$diff.&#039;) for line rowid = &#039;.$obj-&amp;gt;rowid.&amp;quot; (total vat of line calculated=&amp;quot;.$tmpcal[1].&amp;quot;, database=&amp;quot;.$obj-&amp;gt;total_tva.&amp;quot;). We fix the total_vat and total_ttc of line by running sqlfix = &amp;quot;.$sqlfix);&lt;br /&gt;
            $resqlfix=$this-&amp;gt;db-&amp;gt;query($sqlfix);&lt;br /&gt;
            if (! $resqlfix) dol_print_error($this-&amp;gt;db,&#039;Failed to update line&#039;);&lt;br /&gt;
            $obj-&amp;gt;total_tva = $tmpcal[1];&lt;br /&gt;
            $obj-&amp;gt;total_ttc = $tmpcal[2];&lt;br /&gt;
            //&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just after, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
$cf_coeff = $cf_records[$obj-&amp;gt;rowid]-&amp;gt;coefficient;&lt;br /&gt;
if (empty($cf_coeff) and $cf_coeff !== 0) $cf_coeff = 1; // by default, an empty coefficient = 1&lt;br /&gt;
$obj-&amp;gt;total_ht = $obj-&amp;gt;total_ht * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_tva = $obj-&amp;gt;total_tva * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_localtax1 = $obj-&amp;gt;total_localtax1 * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_localtax2 = $obj-&amp;gt;total_localtax2 * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_ttc = $obj-&amp;gt;total_ttc * $cf_coeff;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should do the trick. Now go to one of your invoices (or whatever module you are targetting), and try to add/edit a product/service line to force refresh the total price. You should then see this:&lt;br /&gt;
[[File:Cf_wiki_coeff_remaining_debt.png]]&lt;br /&gt;
&lt;br /&gt;
= Custom tax per product =&lt;br /&gt;
&lt;br /&gt;
This part will show you how to create a static custom tax per product. Static means that the tax will be stored per product, and not per product&#039;s line, thus you just have to setup the tax once per product and it will always be applied in every invoice you add this product.&lt;br /&gt;
&lt;br /&gt;
To do that, you will need the CustomFields Pro module + ProductsEasyAccess.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
1- Create a product&#039;s custom fields called &amp;quot;custom_tax&amp;quot;. Now, using ProductsEasyAccess, you can access this field wherever you wants, either to show it on the Dolibarr&#039;s interface (see previous chapter), or in a PDF template, which we will describe below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2- Modify your invoice&#039;s PDF template. You first need to load all the products&#039; fields &#039;&#039;before&#039;&#039; the loop over the products&#039; lines. To do this, search for a line like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// This should be placed above the &amp;quot;loop lines&amp;quot; below, add this:&lt;br /&gt;
&lt;br /&gt;
dol_include_once(&#039;/productseasyaccess/lib/productseasyaccess.lib.php&#039;);&lt;br /&gt;
fill_object_with_products_fields($object);&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines, search for this command&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  // Add this just below the for loop, this will ease your php manipulation after.&lt;br /&gt;
  // This will allow you to more easily access the products&#039; fields (this will store the id of each product inside $lineproductid).&lt;br /&gt;
  $line_product_id = $object-&amp;gt;lines[$i]-&amp;gt;fk_product;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4- You can now use your products&#039; fields in your PDF. To access the custom_tax field, just use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And to print it in your PDF:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell(0,3, $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax, 0, &#039;L&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5- Then, if you want to count the custom tax total and add it to your vat total, you can create a counter inside the for loop we cited in the first step:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_custom_tax = 0; // Initialize the custom tax total counter&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  $object-&amp;gt;total_custom_tax += $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then find inside the function **_tableau_tot**, find the following line:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And prepend the total modification like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc + $object-&amp;gt;total_custom_tax; // prepend this to add the custom tax to the total&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Done. This should show your custom tax per product and your total including all custom taxes.&lt;/div&gt;</summary>
		<author><name>Lrq3000</name></author>
	</entry>
	<entry>
		<id>https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33073</id>
		<title>Module CustomFields Cases</title>
		<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33073"/>
		<updated>2015-01-15T17:20:19Z</updated>

		<summary type="html">&lt;p&gt;Lrq3000: /* Refresh the price instantly */ Added a note&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:CustomFields]]&lt;br /&gt;
{{TemplateDocDevEn}}&lt;br /&gt;
{{TemplateModEN}}&lt;br /&gt;
{{BasculeDevUserEn|&lt;br /&gt;
name=CustomFields|&lt;br /&gt;
num=8500|&lt;br /&gt;
devdoc=[[Module_CustomFields_Dev]]|&lt;br /&gt;
userdoc=[[Module_CustomFields]]|}}&lt;br /&gt;
This page lists a compilation of concrete, practical examples uses of [[Module_CustomFields|CustomFields]] and of its various features.&lt;br /&gt;
&lt;br /&gt;
If you had an experience with CustomFields, you can also add here your case as an example for others to get inspired.&lt;br /&gt;
&lt;br /&gt;
= How to make a generic PDF template listing all custom fields =&lt;br /&gt;
&lt;br /&gt;
To make a generic PDF template listing all custom fields, you just need to take an existing PDF template, then you need to follow these 3 steps:&lt;br /&gt;
&lt;br /&gt;
1/ Edit the class name (required, else it will conflict with the original template).&lt;br /&gt;
&lt;br /&gt;
Eg: if we take the crabe template for invoices, we change:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;crabe&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
into:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_customfields extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;customfields&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2/ Copy this generic function at the end of the PDF:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propale/order/etc... (CustomFields simpler functions will automatically adapt)&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs); // set default PDF font size&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
    // Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
    $customfields = customfields_fill_object($object, null, $outputlangs, null, true);&lt;br /&gt;
&lt;br /&gt;
    // Setting the starting position of the text cursor&lt;br /&gt;
    $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
    $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
    // Printing the customfields&lt;br /&gt;
    foreach ($object-&amp;gt;customfields as $label=&amp;gt;$value) { // $value is already formatted!&lt;br /&gt;
        // Get translated label&lt;br /&gt;
        $translatedlabel = $customfields-&amp;gt;findLabelPDF($label, $outputlangs); // translated label of the customfield (not translated by default in customfields_fill_object() because a field should always be accessible by a base name, whatever the translation is)&lt;br /&gt;
&lt;br /&gt;
        // PDF formatting, placement and printing&lt;br /&gt;
        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
        $pdf-&amp;gt;MultiCell(0,3, $translatedlabel.&#039; ($object-&amp;gt;customfields-&amp;gt;&#039;.$label.&#039;): &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternative function (with a bit less functionalities, but more controllable):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page (used to debug if CustomFields setup is correct)&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propal/product/whatever...&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs);&lt;br /&gt;
&lt;br /&gt;
            if (empty($object-&amp;gt;table_element) or empty($object-&amp;gt;id)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;Current \$object is not compatible with CustomFields, could not find table_element or id.&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
    $customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element);&lt;br /&gt;
&lt;br /&gt;
            // Fetching custom fields records&lt;br /&gt;
            $fields = $customfields-&amp;gt;fetch($object-&amp;gt;id);&lt;br /&gt;
&lt;br /&gt;
            if (!isset($fields)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;No custom field could be found for this object. Please check your configuration (did you create at least one customfield and set a value in the current datasheet?)&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            } else {&lt;br /&gt;
                // Setting the starting position of the text cursor&lt;br /&gt;
                $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
                $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
                // Printing the customfields&lt;br /&gt;
                foreach ($fields as $key=&amp;gt;$field) {&lt;br /&gt;
                        $translatedname = $customfields-&amp;gt;findLabelPDF($key, $outputlangs); // label of the customfield&lt;br /&gt;
                        $value = $customfields-&amp;gt;simpleprintFieldPDF($key, $field, $outputlangs); // value (cleaned and properly formatted) of the customfield&lt;br /&gt;
&lt;br /&gt;
                        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
                        $pdf-&amp;gt;MultiCell(0,3, $translatedname.&#039;: &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
                        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3/ Call the _pagecustomfields function at the right place (generally, after _pagefoot and just before $pdf-&amp;gt;Close()):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Pied de page&lt;br /&gt;
$this-&amp;gt;_pagefoot($pdf,$object,$outputlangs);&lt;br /&gt;
$pdf-&amp;gt;AliasNbPages();&lt;br /&gt;
&lt;br /&gt;
// CustomFields&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) { // if the customfields module is activated...&lt;br /&gt;
        // Add a page with the customfields&lt;br /&gt;
        $pdf-&amp;gt;AddPage();&lt;br /&gt;
        $this-&amp;gt;_pagecustomfields($pdf,$object,$outputlangs);&lt;br /&gt;
        $pagenb++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
$pdf-&amp;gt;Close(); // you should first search for this, you should only find one occurrence&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then go to the module&#039;s admin panel (eg: invoice admin panel) and enable the template you&#039;ve just created.&lt;br /&gt;
&lt;br /&gt;
You can now use it to test if your customfields work.&lt;br /&gt;
&lt;br /&gt;
= Conditions on fields selection =&lt;br /&gt;
&lt;br /&gt;
Sometimes you may want to show a DropdownBox or Constraint (or another multi-choice type), but you may want to show or hide choices based on conditions (eg: only if it&#039;s a prospect, only if this zone was selected, only if this category was selected, etc..).&lt;br /&gt;
&lt;br /&gt;
To know which is the best solution you should choose to implement your condition, you should first know in which category your condition falls in.&lt;br /&gt;
&lt;br /&gt;
== Conditions categories ==&lt;br /&gt;
&lt;br /&gt;
Conditions can be categorized into 4 types:&lt;br /&gt;
&lt;br /&gt;
* static condition: choices are restrained on a condition that never changes (eg: only show third-parties that are suppliers, or lowercase all data on sql insertion, or check that a field is above a certain number, etc.).&lt;br /&gt;
* semi-dynamic condition: choices that are constrained on a static table but based on the value of another field/customfield. This is also commonly called &amp;quot;Cascaded dropdown lists&amp;quot; or &amp;quot;Dynamically linked dropdown lists&amp;quot;.&lt;br /&gt;
* dynamic: everything is dynamic: choices are based on a subfield of a field of the current module, so that there&#039;s no material table and it&#039;s impossible to create a view (because you would need to create one view per id) (eg: show only the contacts attached to the third-party being accessed)&lt;br /&gt;
* timed condition: a condition that is based on time or recurrence (eg: at 5:00 AM delete old records, etc..)&lt;br /&gt;
&lt;br /&gt;
== Conditions solutions ==&lt;br /&gt;
&lt;br /&gt;
Now that you know what kind of condition you want to set, you can choose one of the following solutions depending on your preferences and knowledge.&lt;br /&gt;
&lt;br /&gt;
In any case, you can always use the &#039;&#039;&#039;CustomFields&#039;s overload functions&#039;&#039;&#039; to fit your needs for nearly every type of condition.&lt;br /&gt;
&lt;br /&gt;
=== Static condition ===&lt;br /&gt;
&lt;br /&gt;
* View: create a view on the table based on the condition, and then create a custom field constrained on this view (which is just like any table), eg: only show third-parties that are suppliers:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
CREATE VIEW llx_societe_supplier_view AS&lt;br /&gt;
SELECT *&lt;br /&gt;
FROM llx_societe&lt;br /&gt;
WHERE fournisseur=1;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
WARNING: foreign keys (and thus constraints) can&#039;t be used on views with MySQL, and materialized views neither exist, so you can&#039;t use this method with MySQL.&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
* SQL Transformations&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
=== Semi-dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade option is ideal in most cases, and if not enough, you can use the Cascade Custom option and Overloading Functions.&lt;br /&gt;
* View&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
&lt;br /&gt;
=== Dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade Custom and Overload functions&lt;br /&gt;
* Making your own module, which calls the CustomFields class&lt;br /&gt;
&lt;br /&gt;
=== Timed condition ===&lt;br /&gt;
&lt;br /&gt;
* SQL scheduled events&lt;br /&gt;
* Cron job&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
= Overloading functions =&lt;br /&gt;
&lt;br /&gt;
Here are a few concrete use cases of overloading functions to achieve various goals.&lt;br /&gt;
&lt;br /&gt;
== Dynamic list selection based on another one ==&lt;br /&gt;
&lt;br /&gt;
The goal is to show two custom fields on the Third-Party module: one which gives the zone (secteur) of the third-party (Isere, Alpes du sud, Haute-Savoie...) and the other which gives relative to the zone the list of all the ski resorts (station_a) inside the selected zone.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will show how to manually manage this kind of requirements using [[Module_CustomFields#Overloading_functions:_Adding_your_own_management_code_for_custom_fields|overloading functions]], but in latest CustomFields releases, the same solution can be achieved automatically using the [[Module_CustomFields#Cascade|Cascade option]].&lt;br /&gt;
&lt;br /&gt;
We have one table &#039;&#039;&#039;llx_k_station&#039;&#039;&#039; (rowid, station (char50), secteur(char50)) which contains the following: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1,les 2 alpes,isère&lt;br /&gt;
2,chamrousse,isère&lt;br /&gt;
3,alpe d&#039;huez,isère&lt;br /&gt;
4,vars,alpes du sud&lt;br /&gt;
5,risoul,alpes du sud&lt;br /&gt;
etc...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In CustomFields&#039;s admin panel, we create two custom fields:&lt;br /&gt;
&lt;br /&gt;
1- secteur: returns the list of all zones - type DropdownBox: enum(&#039;- Aucun&#039;,&#039;Alpes du Sud&#039;,&#039;export&#039;,&#039;Haute-Savoie&#039;,&#039;Isère&#039;, etc...)&lt;br /&gt;
&lt;br /&gt;
2- station_a: returns the list of all ski resorts - type constraint on &#039;&#039;&#039;llx_k_station&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Here is the code to put in customfields_fields_extend.lib.php that will allow to show only the ski resorts corresponding to the selected zone:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_editview_societe_station_a (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$parameters, &amp;amp;$action, &amp;amp;$user, &amp;amp;$idvar, &amp;amp;$rightok, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
  global $db; // allow to access database functions&lt;br /&gt;
  $sql=&amp;quot;SELECT llx_k_station.station, llx_k_station.rowid FROM llx_societe_customfields INNER JOIN llx_k_station ON llx_societe_customfields.Secteur = llx_k_station.secteur WHERE llx_societe_customfields.fk_societe=&amp;quot;.$object-&amp;gt;id;&lt;br /&gt;
$result=$db-&amp;gt;query($sql);&lt;br /&gt;
 &lt;br /&gt;
  if ($result) {&lt;br /&gt;
    $num = $db-&amp;gt;num_rows($result);&lt;br /&gt;
    $i = 0;&lt;br /&gt;
    $myarray = array();&lt;br /&gt;
    while ($i &amp;lt; $num)&lt;br /&gt;
    {&lt;br /&gt;
      $obj = $db-&amp;gt;fetch_object($result);&lt;br /&gt;
      $myarray []=array(&#039;id&#039;=&amp;gt;$obj-&amp;gt;rowid, &#039;value&#039;=&amp;gt;$obj-&amp;gt;station);&lt;br /&gt;
      $i++;&lt;br /&gt;
    }&lt;br /&gt;
    $value = $myarray; &lt;br /&gt;
    $db-&amp;gt;free($result);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to manub for giving this case.&lt;br /&gt;
&lt;br /&gt;
== Dynamic discount computation per products lines on propales ==&lt;br /&gt;
&lt;br /&gt;
The goal here is to compute the discount for a product line in propales based on a computation on three custom fields.&lt;br /&gt;
&lt;br /&gt;
First, create three custom fields for &amp;quot;Line commercial proposals&amp;quot;: vl, cpl and lc.&lt;br /&gt;
&lt;br /&gt;
Then add the following overloading function at the end of your customfields_fields_extend.lib.php file (read the comments for more informations):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_propaldet_vl (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
&lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $vl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;vl&#039;};&lt;br /&gt;
    $cpl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;cpl&#039;};&lt;br /&gt;
    $lc = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;lc&#039;};&lt;br /&gt;
&lt;br /&gt;
    // Compute the discount&lt;br /&gt;
    $remise_calculee = (100 - $vl) * (100 - $cpl) * (100 - $lc);&lt;br /&gt;
&lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $remise_calculee = price2num($remise_calculee);&lt;br /&gt;
&lt;br /&gt;
    // Assign the new discount into the PropalLine object&lt;br /&gt;
    $object-&amp;gt;remise_percent = $remise_calculee;&lt;br /&gt;
&lt;br /&gt;
    // Update the price (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
&lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
&lt;br /&gt;
    //$q = &amp;quot;UPDATE  &amp;quot;.MAIN_DB_PREFIX.&amp;quot;propaldet SET remise_percent=&amp;quot;.$remise_calculee.&amp;quot; WHERE  rowid=&amp;quot;.$object-&amp;gt;rowid;&lt;br /&gt;
    //$db-&amp;gt;query($q);&lt;br /&gt;
    &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;vl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;cpl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;lc&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Linking Dolibarr objects from two different modules =&lt;br /&gt;
&lt;br /&gt;
Dolibarr sometimes offers the possibility to link two different modules. Eg: when you convert an Intervention card into an Invoice.&lt;br /&gt;
&lt;br /&gt;
In this case, you might want in your templates to fetch the custom fields of the linked object. Eg: you generate a PDF from the Invoice, but you want to get the custom fields from the Intervention that was converted into the Invoice.&lt;br /&gt;
&lt;br /&gt;
The key here is to use the &#039;&#039;&#039;llx_element_element&#039;&#039;&#039; table or the &#039;&#039;&#039;llx_element_*&#039;&#039;&#039; tables (eg: llx_element_contact): this is a standard Dolibarr table that stores all links between objects of two different modules.&lt;br /&gt;
&lt;br /&gt;
Here is the structure:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
rowid | fk_source | sourcetype | fk_target | targettype&lt;br /&gt;
-------------------------------------------------------&lt;br /&gt;
1 | 2 | fichinter | 5 | facture&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can then use a php code like the following to populate your $object with customfields:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
// This will also create and returns us an instance of the CustomFields class, which we will use to manually query the llx_element_element table&lt;br /&gt;
$customfields = customfields_fill_object($object, null, $outputlangs, &#039;facture&#039;, true); // beautified values&lt;br /&gt;
&lt;br /&gt;
// Querying the llx_element_element table manually to get the link between the invoice and the linked intervention card (we want the id of the intervention card)&lt;br /&gt;
// fetchAny($columns, $table, $where=&#039;&#039;, $orderby=&#039;&#039;, $limitby=&#039;&#039;)&lt;br /&gt;
$eltrow = $customfields-&amp;gt;fetchAny(&#039;*&#039;, MAIN_DB_PREFIX.&#039;element_element&#039;, &#039;targettype=&amp;quot;&#039;.$object-&amp;gt;element.&#039;&amp;quot; and fk_target=&#039;.$object-&amp;gt;id.&#039; and sourcetype=&amp;quot;fichinter&amp;quot;&#039;);&lt;br /&gt;
&lt;br /&gt;
// Save the intervention&#039;s id&lt;br /&gt;
$sourceid = $eltrow[0][&#039;fk_source&#039;];&lt;br /&gt;
&lt;br /&gt;
// Crafting a dummy intervention object&lt;br /&gt;
$fromobject = new stdClass(); // Init an empty object&lt;br /&gt;
$fromobject-&amp;gt;id = $sourceid; // We need the id and..&lt;br /&gt;
$fromobject-&amp;gt;table_element = &#039;fichinter&#039;; // the table_element (name of the table)&lt;br /&gt;
&lt;br /&gt;
// We can then fill $object with the intervention custom fields (we store them in a sub property &#039;fichinter&#039; to avoid conflicts with invoice&#039;s custom fields)&lt;br /&gt;
customfields_fill_object($object, $fromobject, $outputlangs, &#039;fichinter&#039;, true);&lt;br /&gt;
&lt;br /&gt;
// Now you can access invoices custom fields using:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;facture-&amp;gt;cf_myfield);&lt;br /&gt;
// And access intervention custom fields with:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;fichinter-&amp;gt;cf_myfield);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to netassopro for the tip.&lt;br /&gt;
&lt;br /&gt;
= Modify the total global price with a coefficient (without tampering the discount) =&lt;br /&gt;
&lt;br /&gt;
This is more troublesome than modifying the total price per product/service line, because the total global price of an object (let&#039;s say an invoice, but this also works for any other object) is always recomputed by summing the prices and quantity of each product/service line. This means that although there&#039;s an entry &amp;quot;total_ttc&amp;quot; in the database to store the invoice&#039;s total price, this price is in fact always recomputed, it&#039;s never used as-is. Thus, it&#039;s useless to change only the database record: we need to inject some code to change how the total price is computed.&lt;br /&gt;
&lt;br /&gt;
This could be done using an overloading function or a trigger, but unluckily, Dolibarr doesn&#039;t call a trigger nor a hook when recomputing the price. We thus need to edit core files to do what we want.&lt;br /&gt;
&lt;br /&gt;
However, the bright side is that we can cleverly edit just two functions from one core file: update_price() and getMarginInfos() from /htdocs/core/class/commonobject.class.php , and everything should run smoothly. Beware that if you update Dolibarr, you will need to redo these two modifications in the code.&lt;br /&gt;
&lt;br /&gt;
First, you need to create a new custom field called &amp;quot;global_coefficient&amp;quot; in Invoices (not Invoices Lines!). Then you can continue to the subchapters below.&lt;br /&gt;
&lt;br /&gt;
Also note that this global coefficient is also compatible with per line coefficients (as described in the next chapter), thus you can use a global coefficient and per line coefficients at the same time. You can also use multiple global coefficient if you wish.&lt;br /&gt;
&lt;br /&gt;
Another note: if you have a grid of discounts depending on quantity or price or whatever parameter, you can program a precomputation code using a Custom Ajax function (this way, your discounts will be automatically computed, but then you can still manually enter them for particular cases since the custom field is editable).&lt;br /&gt;
&lt;br /&gt;
== Implement the change of global price in most of Dolibarr (including remaining debt to pay and PDF and ODT) ==&lt;br /&gt;
&lt;br /&gt;
The change we will implement here will reflect the new total price with the global coefficient in most of Dolibarr&#039;s code, since we will here change the main function that is usually called to update and get the total price.&lt;br /&gt;
&lt;br /&gt;
Open /htdocs/core/class/commonobject.class.php and look for the function update_price().&lt;br /&gt;
&lt;br /&gt;
You should see the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function update_price($exclspec=0,$roundingadjust=&#039;none&#039;,$nodatabaseupdate=0,$seller=&#039;&#039;)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just below the include_once line, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;;&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/conf/conf_customfields_func.lib.php&#039;;&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $this-&amp;gt;table_element);&lt;br /&gt;
// Get object&#039;s id (either stored in property &amp;quot;id&amp;quot; or &amp;quot;rowid&amp;quot;)&lt;br /&gt;
$this_id = (!empty($this-&amp;gt;id)) ? $this-&amp;gt;id : $this-&amp;gt;rowid;&lt;br /&gt;
// Load custom fields records&lt;br /&gt;
$cf_parent_record = $customfields-&amp;gt;fetch($this_id);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will load the global_coefficient custom field (and also any other custom field you have defined for invoices).&lt;br /&gt;
&lt;br /&gt;
Now scroll below, and you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
while ($i &amp;lt; $num)&lt;br /&gt;
{&lt;br /&gt;
    $obj = $this-&amp;gt;db-&amp;gt;fetch_object($resql);&lt;br /&gt;
&lt;br /&gt;
    ... (lots of code)&lt;br /&gt;
&lt;br /&gt;
    i++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Add revenue stamp to total&lt;br /&gt;
$this-&amp;gt;total_ttc       += isset($this-&amp;gt;revenuestamp)?$this-&amp;gt;revenuestamp:0;&lt;br /&gt;
&lt;br /&gt;
$this-&amp;gt;db-&amp;gt;free($resql);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is the end of the loop that is summing each product/service line price to compute the total price. Just after this loop, you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Now update global field total_ht, total_ttc and tva&lt;br /&gt;
$fieldht=&#039;total_ht&#039;;&lt;br /&gt;
$fieldtva=&#039;tva&#039;;&lt;br /&gt;
$fieldlocaltax1=&#039;localtax1&#039;;&lt;br /&gt;
$fieldlocaltax2=&#039;localtax2&#039;;&lt;br /&gt;
$fieldttc=&#039;total_ttc&#039;;&lt;br /&gt;
// Specific code for backward compatibility with old field names&lt;br /&gt;
if ($this-&amp;gt;element == &#039;facture&#039; || $this-&amp;gt;element == &#039;facturerec&#039;)             $fieldht=&#039;total&#039;;&lt;br /&gt;
if ($this-&amp;gt;element == &#039;facture_fourn&#039; || $this-&amp;gt;element == &#039;invoice_supplier&#039;) $fieldtva=&#039;total_tva&#039;;&lt;br /&gt;
if ($this-&amp;gt;element == &#039;propal&#039;)                                                $fieldttc=&#039;total&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Between those two blocks of code (so just above // Now update global field total_ht, total_ttc and tva), you can place the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
if (!empty($cf_parent_record-&amp;gt;global_coefficient)) { // avoid applying the coefficient if empty (this will return a 0 price)&lt;br /&gt;
    $this-&amp;gt;total_ht = $this-&amp;gt;total_ht * $cf_parent_record-&amp;gt;global_coefficient;&lt;br /&gt;
    $this-&amp;gt;total_tva = $this-&amp;gt;total_tva * $cf_parent_record-&amp;gt;global_coefficient;&lt;br /&gt;
    $this-&amp;gt;total_ttc = $this-&amp;gt;total_ttc * $cf_parent_record-&amp;gt;global_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Done, now try to add/edit a product/service line, and the total price should be affected by the global coefficient.&lt;br /&gt;
&lt;br /&gt;
== Implementing the change of price in margin module ==&lt;br /&gt;
&lt;br /&gt;
If you enabled the margin module, you should see that the coefficient is not applied there, but it&#039;s easy to fix that.&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/class/commonobject.class.php and find the function getMarginInfos(), then inside find the following lines:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach($this-&amp;gt;lines as $line) {&lt;br /&gt;
    if (empty($line-&amp;gt;pa_ht) &amp;amp;&amp;amp; isset($line-&amp;gt;fk_fournprice) &amp;amp;&amp;amp; !$force_price) {&lt;br /&gt;
        $product = new ProductFournisseur($this-&amp;gt;db);&lt;br /&gt;
        if ($product-&amp;gt;fetch_product_fournisseur_price($line-&amp;gt;fk_fournprice))&lt;br /&gt;
            $line-&amp;gt;pa_ht = $product-&amp;gt;fourn_unitprice * (1 - $product-&amp;gt;fourn_remise_percent / 100);&lt;br /&gt;
        if (isset($conf-&amp;gt;global-&amp;gt;MARGIN_TYPE) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;MARGIN_TYPE == &amp;quot;2&amp;quot; &amp;amp;&amp;amp; $product-&amp;gt;fourn_unitcharges &amp;gt; 0)&lt;br /&gt;
            $line-&amp;gt;pa_ht += $product-&amp;gt;fourn_unitcharges;&lt;br /&gt;
    }&lt;br /&gt;
    // si prix d&#039;achat non renseigné et devrait l&#039;être, alors prix achat = prix vente&lt;br /&gt;
    if ((!isset($line-&amp;gt;pa_ht) || $line-&amp;gt;pa_ht == 0) &amp;amp;&amp;amp; $line-&amp;gt;subprice &amp;gt; 0 &amp;amp;&amp;amp; (isset($conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull == 1)) {&lt;br /&gt;
        $line-&amp;gt;pa_ht = $line-&amp;gt;subprice * (1 - ($line-&amp;gt;remise_percent / 100));&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the lines are filled with margin infos. The price has just been fetched here, and we will now modify it with the coefficient before the price gets used for margin computation.&lt;br /&gt;
&lt;br /&gt;
To optimize things up, we will load the custom fields before the loop (because they don&#039;t change between product/service lines, they are the custom fields of the parent object), and then apply the global_coefficient inside the loop.&lt;br /&gt;
&lt;br /&gt;
Just above the code we found (just before the for loop), paste the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
// Get object&#039;s id (either stored in property &amp;quot;id&amp;quot; or &amp;quot;rowid&amp;quot;)&lt;br /&gt;
$this_id = (!empty($this-&amp;gt;id)) ? $this-&amp;gt;id : $this-&amp;gt;rowid;&lt;br /&gt;
// Load custom fields records&lt;br /&gt;
$customfields = customfields_fill_object($this);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And now, just below the code we found, inside the loop (and just above the comment: &amp;quot;// calcul des marges&amp;quot;), paste the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
// Apply the coefficient&lt;br /&gt;
if (!empty($this-&amp;gt;customfields-&amp;gt;cf_global_coefficient)) {&lt;br /&gt;
    $line-&amp;gt;pa_ht = $line-&amp;gt;pa_ht * $this-&amp;gt;customfields-&amp;gt;cf_global_coefficient;&lt;br /&gt;
    $line-&amp;gt;subprice = $line-&amp;gt;subprice * $this-&amp;gt;customfields-&amp;gt;cf_global_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that here we cheated a bit: we do not compute the coefficient on the global total price, but rather per product/service line price. This is done on purpose, because mathematically this should produce no difference, and it also allows Dolibarr to continue peacefully with its margin calculations (else we would have to adapt all those computations, which would imply a lot more of code changes!).&lt;br /&gt;
&lt;br /&gt;
That&#039;s all, the price should be reflected in the margin. Just refresh the page to reflect the change.&lt;br /&gt;
&lt;br /&gt;
== Refresh the price instantly ==&lt;br /&gt;
&lt;br /&gt;
Maybe you noticed one glitch: when you modify the global_coefficient, the global total price isn&#039;t updated until you add/edit a product/service line. This is because the update_price() function is only called when we add/edit a product/service line.&lt;br /&gt;
&lt;br /&gt;
To fix that, we need now to force the refreshing of price. To do that, we will use an &amp;quot;aftersave&amp;quot; overloading function, which will execute commands after our custom field is saved.&lt;br /&gt;
&lt;br /&gt;
Go to the folder /htdocs/customfields/fields and rename the file customfields_fields_extend.lib.php.example into customfields_fields_extend.lib.php .&lt;br /&gt;
&lt;br /&gt;
Now, open it in your favourite text editor, and add the following function :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_aftersave_facture_global_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value, $fields) {&lt;br /&gt;
    // Force refreshing total price&lt;br /&gt;
    $object-&amp;gt;parent_object-&amp;gt;update_price(0, &#039;auto&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s all. Now, when you edit your global_coefficient, this will force refresh the total price.&lt;br /&gt;
&lt;br /&gt;
However, you will still notice one last glitch: when you edit the global_coefficient, the total price isn&#039;t reflected instantly, you must refresh the webpage once to see the new price. This is because of how the datasheet and the CustomFields hook are designed: CustomFields is called only exactly where the custom fields are place, thus it is called after the total price, the margin and the remaining debt are printed. Thus, CustomFields updates the value of your custom field only after all those informations get printed, and thus it cannot modify them at this stage. That&#039;s why those information are out-of-date, and you must refresh the webpage once to get to see the latest values.&lt;br /&gt;
&lt;br /&gt;
There is nothing you can do about it. One way to fix that would be to modify how CustomFields save the custom fields, by moving this stage at the very top. However, this would need another hook, which should be called at the very beginning of the page loading.&lt;br /&gt;
&lt;br /&gt;
= Modify the total price PER product/service line with a coefficient (without tampering the discount) =&lt;br /&gt;
&lt;br /&gt;
Here we will go through the creation and management of a custom field called &#039;&#039;&#039;coefficient&#039;&#039;&#039; that will change the total price of each product by modifying it by the coefficient.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will not create a coefficient per product but rather per product&#039;s line. Thus if you want to create a coefficient that never change between invoices but only depending on the product and stays static, please refer to the next chapter below about creating a custom tax per product.&lt;br /&gt;
&lt;br /&gt;
The preliminary and necessary step is to first create a custom field that will be used to modify the total price. We will create one for Client Invoice Lines.&lt;br /&gt;
&lt;br /&gt;
Go to the CustomFields administration panel, then into the Client Invoice Lines tab, then click on the button New Field, name the field &#039;&#039;&#039;coefficient&#039;&#039;&#039;, and set the type to &#039;&#039;&#039;Double&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
When you go to client invoices in Dolibarr, you should then see something similar to this:&lt;br /&gt;
[[File:Cfe1.jpg]]&lt;br /&gt;
You can see that our coefficient field was successfully created.&lt;br /&gt;
&lt;br /&gt;
Then here are two alternative ways of implementing the change of the total price of each product relativery to this coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change total price in database ==&lt;br /&gt;
&lt;br /&gt;
Here we will use a &amp;quot;save&amp;quot; overloading function to change the total price directly in the database, thus we won&#039;t have to modify anything else (eg: nor PDF templating nor ODT) since the total price will be already updated in the database.&lt;br /&gt;
&lt;br /&gt;
Warning: this method will work only with CustomFields =&amp;gt; 3.2.23 since the &amp;quot;save&amp;quot; overloading function did not work for products lines before.&lt;br /&gt;
&lt;br /&gt;
Just add the following overloading function in your customfields_fields_extend.lib.php file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_facturedet_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
 &lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $coefficient = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;coefficient&#039;}; // you could also use $coefficient = $value;&lt;br /&gt;
 &lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $coefficient = price2num($coefficient);&lt;br /&gt;
 &lt;br /&gt;
    // Get the original subprices (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
 &lt;br /&gt;
    // Update the subprices with the coefficient&lt;br /&gt;
    $object-&amp;gt;total_ht = $object-&amp;gt;total_ht * $coefficient; // compute no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva = $object-&amp;gt;total_tva * $coefficient; // compute tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc * $coefficient; // compute tax included price&lt;br /&gt;
 &lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(&#039;&#039;,true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
 &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;coefficient&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should show the correct total price on the Dolibarr&#039;s interface without modifying the unit subprice (P.U. Unit):&lt;br /&gt;
&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
You have nothing to edit in your PDF or ODT template, since the new subprice with applied coefficient will be automatically printed instead of the old one without coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change printed total price per product without tampering the subprice in the database ==&lt;br /&gt;
&lt;br /&gt;
Here is a way to change the printed total price (the one shown on screen and in PDF/ODT) but without changing the total price in the database.&lt;br /&gt;
&lt;br /&gt;
The following sub-chapters are all independent, thus you can only follow the ones that you require, eg: if you only need to change the price in your PDF documents, just create a custom field then jump to the sub-chapter to &#039;&#039;&#039;Implement the change of price in PDF&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in ODT templates ===&lt;br /&gt;
&lt;br /&gt;
There&#039;s no eval implementation currently inside ODT templates, but there is a kind of hook implementation, called [[Create_an_ODT_document_template#Other_personalized_tags|substitution functions]].&lt;br /&gt;
&lt;br /&gt;
You can either create your own module, then create your own substitution function, but for the sake of simplicity in this tutorial, we will directly use the CustomFields substitution function. However, please remember that you will have to redo your changes everytime you will update the CustomFields module, so if you don&#039;t want to do that it would be better if you create your own module and substitution function.&lt;br /&gt;
&lt;br /&gt;
Open the file &#039;&#039;&#039;htdocs/customfields/core/substitutions/functions_customfields.lib.php&#039;&#039;&#039; and go to the function &#039;&#039;&#039;customfields_completesubstitutionarray_lines&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
At the very end of this function, but inside the customfields check:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) {&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
But just before the ending bracket, you should add the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($object-&amp;gt;table_element_line === &#039;facturedet&#039;) { // First check that we are in the right module, we don&#039;t want to use coefficient for other modules if we didn&#039;t define the custom field there&lt;br /&gt;
    $substitutionarray[&#039;line_price_full&#039;] = $substitutionarray[&#039;line_price_ttc&#039;] * $substitutionarray[&#039;cf_coefficient&#039;]; // multiply the total price with our coefficient&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code will add a new tag &#039;&#039;&#039;{line_price_full}&#039;&#039;&#039; that you can then use inside your ODT templates.&lt;br /&gt;
&lt;br /&gt;
Just place this tag in a table in your ODT document, between the table tags ( [!-- BEGIN row.lines --] ... [!-- END row.lines --] ), and this will print your full price.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in PDF templates ===&lt;br /&gt;
&lt;br /&gt;
First, we will create our own PDF template by copying an existing template, and then we will edit it to change the price of items.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Creating your own PDF template by copying an existing template&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Go into the following folder: &#039;&#039;&#039;htdocs/core/modules/facture/doc/&#039;&#039;&#039;, then copy the file &#039;&#039;&#039;pdf_crabe.modules.php&#039;&#039;&#039; and name the new one &#039;&#039;&#039;pdf_crabecoeff.modules.php&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Then open it in your favourite editor, and change the following lines:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures // change pdf_crabe into pdf_crabecoeff&lt;br /&gt;
{&lt;br /&gt;
...&lt;br /&gt;
    function __construct($db)&lt;br /&gt;
    {&lt;br /&gt;
        ...&lt;br /&gt;
	$this-&amp;gt;name = &amp;quot;crabe&amp;quot;; // change &amp;quot;crabe&amp;quot; into &amp;quot;crabecoeff&amp;quot;&lt;br /&gt;
        ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you have your own PDF template!&lt;br /&gt;
&lt;br /&gt;
Now you should &#039;&#039;&#039;enable your PDF template&#039;&#039;&#039; before doing anything else, by going into the Modules Administration panel, and into the Invoice admin page, then click on the ON button at the right of the &#039;&#039;&#039;crabecoeff&#039;&#039;&#039; entry to enable the template.&lt;br /&gt;
&lt;br /&gt;
Now, let&#039;s proceed onto modifying the prices.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Changing the prices automatically in your PDF&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
You can now do pretty much anything you want in your PDF template, but I will give you a mean to automatically change the prices of every products with our coefficient, as well as the total price, without editing too much the template.&lt;br /&gt;
&lt;br /&gt;
First, try to find the following code of line (should be inside the write_file() method):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf=pdf_getInstance($this-&amp;gt;format);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, just copy/paste the following code &#039;&#039;&#039;below&#039;&#039;&#039; pdf_getInstance():&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$this-&amp;gt;customfields = customfields_fill_object($object, null, $outputlangs, null, true); // beautified values&lt;br /&gt;
$this-&amp;gt;customfields_raw = customfields_fill_object($object, null, $outputlangs, &#039;raw&#039;, null); // raw values&lt;br /&gt;
$this-&amp;gt;customfields_lines = customfields_fill_object_lines($object, null, $outputlangs, null, true); // product lines&#039; values&lt;br /&gt;
&lt;br /&gt;
// Resetting every $object price because we will compute them anew&lt;br /&gt;
$object-&amp;gt;total_ht = 0;&lt;br /&gt;
$object-&amp;gt;total_tva = 0;&lt;br /&gt;
$object-&amp;gt;total_ttc = 0;&lt;br /&gt;
// For each line of product/service, we will recompute all the prices&lt;br /&gt;
foreach($object-&amp;gt;lines as $line) {&lt;br /&gt;
    // Get the coefficient custom field&#039;s value for the current line&lt;br /&gt;
    $coeff = $object-&amp;gt;customfields-&amp;gt;lines-&amp;gt;{$line-&amp;gt;rowid}-&amp;gt;cf_coefficient;&lt;br /&gt;
    // Recompute the price of this product/service line&lt;br /&gt;
    $line-&amp;gt;total_ht = $line-&amp;gt;total_ht*$coeff; // compute no tax price&lt;br /&gt;
    $line-&amp;gt;total_tva = $line-&amp;gt;total_tva*$coeff; // compute tax amount&lt;br /&gt;
    $line-&amp;gt;total_ttc = $line-&amp;gt;total_ttc*$coeff; // compute tax included price&lt;br /&gt;
    // Do the sum of all new products/services prices and tva (will be incremented with every product/service line)&lt;br /&gt;
    $object-&amp;gt;total_ht += $line-&amp;gt;total_ht; // global no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva += $line-&amp;gt;total_tva; // global vat/tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc += $line-&amp;gt;total_ttc; // global  tax included price&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you should try to generate your PDF document, and you should get something like this:&lt;br /&gt;
[[File:Cfe3.jpg]]&lt;br /&gt;
&lt;br /&gt;
Note: printing the coefficient as a column in your PDF document is also possible but will not be covered here because PDF templates will vary for every persons depending on your needs.&lt;br /&gt;
For guidelines about how to do this, you can read the documentation on [[Create_a_PDF_document_template|how to create a PDF template]] and [[Module_CustomFields#Implementing_in_PDF_templates|how to use custom fields in PDF templates]].&lt;br /&gt;
&lt;br /&gt;
Hint: you should be looking inside the following conditional block, inside the write_file() method:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
{&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of total price in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
Unluckily there is not (yet) any hooking context to modify how lines of products/services are printed, thus the only thing you can do is directly edit the &#039;&#039;&#039;template file&#039;&#039;&#039; and manually put inside your code to load your custom field and print it the way you want using HTML formatting, for our example:&lt;br /&gt;
&lt;br /&gt;
* For Dolibarr 3.3 and above, edit the file &#039;&#039;&#039;htdocs/core/tpl/objectline_view.tpl.php&#039;&#039;&#039;&lt;br /&gt;
* For Dolibarr 3.1 and 3.2, edit the file &#039;&#039;&#039;htdocs/core/tpl/freeproductline_view.tpl.php&#039;&#039;&#039; AND &#039;&#039;&#039;predefinedproductline_view.tpl.php&#039;&#039;&#039; (apply the same changes to both files)&lt;br /&gt;
&lt;br /&gt;
In the template file you opened, find the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And replace it by:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $object-&amp;gt;table_element_line; // you can use $object-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
&lt;br /&gt;
// Modifying the HT price and printing it on the Dolibarr&#039;s datasheet interface&lt;br /&gt;
// Applying a multiplying/dividing coefficient to an HT price will give a mathematically correct TTC price since the taxes are also a multiplier&lt;br /&gt;
// However, if you want to apply other mathematical operations (like addition or substaction), you should use the total price with $line-&amp;gt;total_ttc&lt;br /&gt;
if (!empty($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
    echo price($line-&amp;gt;total_ht * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient);&lt;br /&gt;
} else {&lt;br /&gt;
    echo price($line-&amp;gt;total_ht);&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should produce something like this:&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Technical note: here we are using customfields_fill_object() instead of customfields_fill_object_lines() (the latter being specifically adapted to lines and thus should theoretically be used here) for performance reasons because the template file we are editing is called in a loop, and customfields_fill_object_lines() should be called outside a loop (because it already does the job that is done by the loop), so here we &#039;tricks&#039; the customfields_fill_object() function to avoid this loop by submitting &#039;facturedet&#039; as a module by itself (when in fact it&#039;s a product lines table, but it works the same way nevertheless). This is a good demonstration of the flexibility of the CustomFields functions and methods.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the coefficient column in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
It&#039;s also possible to show the coefficient in a new column on the Dolibarr interface.&lt;br /&gt;
&lt;br /&gt;
To do that, you need to do two things:&lt;br /&gt;
&lt;br /&gt;
1- Add the column title &amp;quot;Coeff.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
2- Add the generator that will input the coefficient value for each line.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Add the column title &amp;quot;Coeff.&amp;quot;&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
Open in your favourite editor the file /htdocs/core/class/commonobject.class.php and inside the function printObjectLines (note the plural s), find the following lines of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Total HT&lt;br /&gt;
print &#039;&amp;lt;td align=&amp;quot;right&amp;quot; width=&amp;quot;50&amp;quot;&amp;gt;&#039;.$langs-&amp;gt;trans(&#039;TotalHTShort&#039;).&#039;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is what prints the Total HT price column. We will place the Coeff column just before.&lt;br /&gt;
&lt;br /&gt;
So, just above this code, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
dol_include_once(&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element_line);&lt;br /&gt;
$coeff_field = $customfields-&amp;gt;fetchFieldStruct(&#039;coefficient&#039;);&lt;br /&gt;
if (!empty($coeff_field)) {&lt;br /&gt;
    print &#039;&amp;lt;td align=&amp;quot;right&amp;quot; width=&amp;quot;50&amp;quot;&amp;gt;&#039;.$langs-&amp;gt;trans(&#039;Coeff.&#039;).&#039;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will take care of showing the Coeff. column, but only for modules where you created such a custom field (because the file we modified is a common object for all modules in Dolibarr, not just invoices, thus this modification will gracefully skip this custom field if it does not exists for other modules).&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Add the generator&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/tpl/objectline_view.tpl.php and try to find the following block of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php if ($line-&amp;gt;special_code == 3)	{ ?&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo $langs-&amp;gt;trans(&#039;Option&#039;); ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } else { ?&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is what prints the total HT price value for each line.&lt;br /&gt;
&lt;br /&gt;
Just above of this block of code, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $object-&amp;gt;table_element_line; // you can use $this-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
if (isset($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
    &amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient; ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just like for the column title, this code will take care of generating the coefficient value only if the custom field is defined, because this file is also a generic template for all modules of Dolibarr.&lt;br /&gt;
&lt;br /&gt;
At this point, everything is done, you should see the following:&lt;br /&gt;
[[File:Cf_wiki_coeff_column.png]]&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in margin module ===&lt;br /&gt;
&lt;br /&gt;
If you enabled the margin module, you should see that the coefficient is not applied there, but it&#039;s easy to fix that.&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/class/commonobject.class.php and find the function getMarginInfos(), then inside find the following lines:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach($this-&amp;gt;lines as $line) {&lt;br /&gt;
    if (empty($line-&amp;gt;pa_ht) &amp;amp;&amp;amp; isset($line-&amp;gt;fk_fournprice) &amp;amp;&amp;amp; !$force_price) {&lt;br /&gt;
        $product = new ProductFournisseur($this-&amp;gt;db);&lt;br /&gt;
        if ($product-&amp;gt;fetch_product_fournisseur_price($line-&amp;gt;fk_fournprice))&lt;br /&gt;
            $line-&amp;gt;pa_ht = $product-&amp;gt;fourn_unitprice * (1 - $product-&amp;gt;fourn_remise_percent / 100);&lt;br /&gt;
        if (isset($conf-&amp;gt;global-&amp;gt;MARGIN_TYPE) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;MARGIN_TYPE == &amp;quot;2&amp;quot; &amp;amp;&amp;amp; $product-&amp;gt;fourn_unitcharges &amp;gt; 0)&lt;br /&gt;
            $line-&amp;gt;pa_ht += $product-&amp;gt;fourn_unitcharges;&lt;br /&gt;
    }&lt;br /&gt;
    // si prix d&#039;achat non renseigné et devrait l&#039;être, alors prix achat = prix vente&lt;br /&gt;
    if ((!isset($line-&amp;gt;pa_ht) || $line-&amp;gt;pa_ht == 0) &amp;amp;&amp;amp; $line-&amp;gt;subprice &amp;gt; 0 &amp;amp;&amp;amp; (isset($conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull == 1)) {&lt;br /&gt;
        $line-&amp;gt;pa_ht = $line-&amp;gt;subprice * (1 - ($line-&amp;gt;remise_percent / 100));&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the lines are filled with margin infos. The price has just been fetched here, and we will now modify it with the coefficient before the price gets used for margin computation.&lt;br /&gt;
&lt;br /&gt;
Just below the code we found, paste the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $line-&amp;gt;table_element;&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
if (!empty($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
    $line-&amp;gt;pa_ht = $line-&amp;gt;pa_ht * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient;&lt;br /&gt;
    $line-&amp;gt;subprice = $line-&amp;gt;subprice * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s all, the price should be reflected in the margin.&lt;br /&gt;
[[File:Cf_wiki_coeff_margin.png]]&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in remaining debt to pay ===&lt;br /&gt;
&lt;br /&gt;
Open /htdocs/core/class/commonobject.class.php and look for the function update_price().&lt;br /&gt;
&lt;br /&gt;
You should see the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function update_price($exclspec=0,$roundingadjust=&#039;none&#039;,$nodatabaseupdate=0,$seller=&#039;&#039;)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just below the include_once line, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;;&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/conf/conf_customfields_func.lib.php&#039;;&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $this-&amp;gt;table_element_line);&lt;br /&gt;
// Fetching line ids from parent object id&lt;br /&gt;
$line_ids = $customfields-&amp;gt;fetchAny(&#039;rowid&#039;, MAIN_DB_PREFIX.$this-&amp;gt;table_element_line, $this-&amp;gt;fk_element.&#039; = &#039;.$this-&amp;gt;id);&lt;br /&gt;
$line_ids = array_values_recursive(&#039;rowid&#039;, $line_ids);&lt;br /&gt;
// Load custom fields&lt;br /&gt;
$cf_records = $customfields-&amp;gt;fetch($line_ids);&lt;br /&gt;
// Reassociate the records with the line id (instead of the customfields record id)&lt;br /&gt;
$cf_line_cfrowid_to_rowid = array_reassociate(&#039;rowid&#039;, &#039;fk_&#039;.$this-&amp;gt;table_element_line, $cf_records);&lt;br /&gt;
$tmp = $cf_records;&lt;br /&gt;
$cf_records = array();&lt;br /&gt;
foreach ($tmp as $k=&amp;gt;$v) { $cf_records[$cf_line_cfrowid_to_rowid[$k]] = $tmp[$k]; }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will load the coefficient custom field (and also any other custom field you have defined for lines).&lt;br /&gt;
&lt;br /&gt;
Now scroll below, and you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
while ($i &amp;lt; $num)&lt;br /&gt;
{&lt;br /&gt;
    $obj = $this-&amp;gt;db-&amp;gt;fetch_object($resql);&lt;br /&gt;
&lt;br /&gt;
    // Note: There is no check on detail line and no check on total, if $forcedroundingmode = &#039;none&#039;&lt;br /&gt;
&lt;br /&gt;
    if ($forcedroundingmode == &#039;0&#039;)	// Check if data on line are consistent. This may solve lines that were not consistent because set with $forcedroundingmode=&#039;auto&#039;&lt;br /&gt;
    {&lt;br /&gt;
        $localtax_array=array($obj-&amp;gt;localtax1_type,$obj-&amp;gt;localtax1_tx,$obj-&amp;gt;localtax2_type,$obj-&amp;gt;localtax2_tx);&lt;br /&gt;
        $tmpcal=calcul_price_total($obj-&amp;gt;qty, $obj-&amp;gt;up, $obj-&amp;gt;remise_percent, $obj-&amp;gt;vatrate, $obj-&amp;gt;localtax1_tx, $obj-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $obj-&amp;gt;info_bits, $obj-&amp;gt;product_type, $seller, $localtax_array);&lt;br /&gt;
        $diff=price2num($tmpcal[1] - $obj-&amp;gt;total_tva, &#039;MT&#039;, 1);&lt;br /&gt;
        if ($diff)&lt;br /&gt;
        {&lt;br /&gt;
            $sqlfix=&amp;quot;UPDATE &amp;quot;.MAIN_DB_PREFIX.$this-&amp;gt;table_element_line.&amp;quot; SET &amp;quot;.$fieldtva.&amp;quot; = &amp;quot;.$tmpcal[1].&amp;quot;, total_ttc = &amp;quot;.$tmpcal[2].&amp;quot; WHERE rowid = &amp;quot;.$obj-&amp;gt;rowid;&lt;br /&gt;
            dol_syslog(&#039;We found unconsistent data into detailed line (difference of &#039;.$diff.&#039;) for line rowid = &#039;.$obj-&amp;gt;rowid.&amp;quot; (total vat of line calculated=&amp;quot;.$tmpcal[1].&amp;quot;, database=&amp;quot;.$obj-&amp;gt;total_tva.&amp;quot;). We fix the total_vat and total_ttc of line by running sqlfix = &amp;quot;.$sqlfix);&lt;br /&gt;
            $resqlfix=$this-&amp;gt;db-&amp;gt;query($sqlfix);&lt;br /&gt;
            if (! $resqlfix) dol_print_error($this-&amp;gt;db,&#039;Failed to update line&#039;);&lt;br /&gt;
            $obj-&amp;gt;total_tva = $tmpcal[1];&lt;br /&gt;
            $obj-&amp;gt;total_ttc = $tmpcal[2];&lt;br /&gt;
            //&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just after, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
$cf_coeff = $cf_records[$obj-&amp;gt;rowid]-&amp;gt;coefficient;&lt;br /&gt;
if (empty($cf_coeff) and $cf_coeff !== 0) $cf_coeff = 1; // by default, an empty coefficient = 1&lt;br /&gt;
$obj-&amp;gt;total_ht = $obj-&amp;gt;total_ht * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_tva = $obj-&amp;gt;total_tva * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_localtax1 = $obj-&amp;gt;total_localtax1 * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_localtax2 = $obj-&amp;gt;total_localtax2 * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_ttc = $obj-&amp;gt;total_ttc * $cf_coeff;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should do the trick. Now go to one of your invoices (or whatever module you are targetting), and try to add/edit a product/service line to force refresh the total price. You should then see this:&lt;br /&gt;
[[File:Cf_wiki_coeff_remaining_debt.png]]&lt;br /&gt;
&lt;br /&gt;
= Custom tax per product =&lt;br /&gt;
&lt;br /&gt;
This part will show you how to create a static custom tax per product. Static means that the tax will be stored per product, and not per product&#039;s line, thus you just have to setup the tax once per product and it will always be applied in every invoice you add this product.&lt;br /&gt;
&lt;br /&gt;
To do that, you will need the CustomFields Pro module + ProductsEasyAccess.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
1- Create a product&#039;s custom fields called &amp;quot;custom_tax&amp;quot;. Now, using ProductsEasyAccess, you can access this field wherever you wants, either to show it on the Dolibarr&#039;s interface (see previous chapter), or in a PDF template, which we will describe below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2- Modify your invoice&#039;s PDF template. You first need to load all the products&#039; fields &#039;&#039;before&#039;&#039; the loop over the products&#039; lines. To do this, search for a line like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// This should be placed above the &amp;quot;loop lines&amp;quot; below, add this:&lt;br /&gt;
&lt;br /&gt;
dol_include_once(&#039;/productseasyaccess/lib/productseasyaccess.lib.php&#039;);&lt;br /&gt;
fill_object_with_products_fields($object);&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines, search for this command&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  // Add this just below the for loop, this will ease your php manipulation after.&lt;br /&gt;
  // This will allow you to more easily access the products&#039; fields (this will store the id of each product inside $lineproductid).&lt;br /&gt;
  $line_product_id = $object-&amp;gt;lines[$i]-&amp;gt;fk_product;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4- You can now use your products&#039; fields in your PDF. To access the custom_tax field, just use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And to print it in your PDF:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell(0,3, $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax, 0, &#039;L&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5- Then, if you want to count the custom tax total and add it to your vat total, you can create a counter inside the for loop we cited in the first step:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_custom_tax = 0; // Initialize the custom tax total counter&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  $object-&amp;gt;total_custom_tax += $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then find inside the function **_tableau_tot**, find the following line:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And prepend the total modification like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc + $object-&amp;gt;total_custom_tax; // prepend this to add the custom tax to the total&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Done. This should show your custom tax per product and your total including all custom taxes.&lt;/div&gt;</summary>
		<author><name>Lrq3000</name></author>
	</entry>
	<entry>
		<id>https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33072</id>
		<title>Module CustomFields Cases</title>
		<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33072"/>
		<updated>2015-01-15T17:13:52Z</updated>

		<summary type="html">&lt;p&gt;Lrq3000: /* Implementing the change of price in margin module */ Adding an overloading function to refresh the prices instantly&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:CustomFields]]&lt;br /&gt;
{{TemplateDocDevEn}}&lt;br /&gt;
{{TemplateModEN}}&lt;br /&gt;
{{BasculeDevUserEn|&lt;br /&gt;
name=CustomFields|&lt;br /&gt;
num=8500|&lt;br /&gt;
devdoc=[[Module_CustomFields_Dev]]|&lt;br /&gt;
userdoc=[[Module_CustomFields]]|}}&lt;br /&gt;
This page lists a compilation of concrete, practical examples uses of [[Module_CustomFields|CustomFields]] and of its various features.&lt;br /&gt;
&lt;br /&gt;
If you had an experience with CustomFields, you can also add here your case as an example for others to get inspired.&lt;br /&gt;
&lt;br /&gt;
= How to make a generic PDF template listing all custom fields =&lt;br /&gt;
&lt;br /&gt;
To make a generic PDF template listing all custom fields, you just need to take an existing PDF template, then you need to follow these 3 steps:&lt;br /&gt;
&lt;br /&gt;
1/ Edit the class name (required, else it will conflict with the original template).&lt;br /&gt;
&lt;br /&gt;
Eg: if we take the crabe template for invoices, we change:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;crabe&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
into:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_customfields extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;customfields&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2/ Copy this generic function at the end of the PDF:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propale/order/etc... (CustomFields simpler functions will automatically adapt)&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs); // set default PDF font size&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
    // Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
    $customfields = customfields_fill_object($object, null, $outputlangs, null, true);&lt;br /&gt;
&lt;br /&gt;
    // Setting the starting position of the text cursor&lt;br /&gt;
    $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
    $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
    // Printing the customfields&lt;br /&gt;
    foreach ($object-&amp;gt;customfields as $label=&amp;gt;$value) { // $value is already formatted!&lt;br /&gt;
        // Get translated label&lt;br /&gt;
        $translatedlabel = $customfields-&amp;gt;findLabelPDF($label, $outputlangs); // translated label of the customfield (not translated by default in customfields_fill_object() because a field should always be accessible by a base name, whatever the translation is)&lt;br /&gt;
&lt;br /&gt;
        // PDF formatting, placement and printing&lt;br /&gt;
        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
        $pdf-&amp;gt;MultiCell(0,3, $translatedlabel.&#039; ($object-&amp;gt;customfields-&amp;gt;&#039;.$label.&#039;): &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternative function (with a bit less functionalities, but more controllable):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page (used to debug if CustomFields setup is correct)&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propal/product/whatever...&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs);&lt;br /&gt;
&lt;br /&gt;
            if (empty($object-&amp;gt;table_element) or empty($object-&amp;gt;id)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;Current \$object is not compatible with CustomFields, could not find table_element or id.&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
    $customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element);&lt;br /&gt;
&lt;br /&gt;
            // Fetching custom fields records&lt;br /&gt;
            $fields = $customfields-&amp;gt;fetch($object-&amp;gt;id);&lt;br /&gt;
&lt;br /&gt;
            if (!isset($fields)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;No custom field could be found for this object. Please check your configuration (did you create at least one customfield and set a value in the current datasheet?)&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            } else {&lt;br /&gt;
                // Setting the starting position of the text cursor&lt;br /&gt;
                $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
                $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
                // Printing the customfields&lt;br /&gt;
                foreach ($fields as $key=&amp;gt;$field) {&lt;br /&gt;
                        $translatedname = $customfields-&amp;gt;findLabelPDF($key, $outputlangs); // label of the customfield&lt;br /&gt;
                        $value = $customfields-&amp;gt;simpleprintFieldPDF($key, $field, $outputlangs); // value (cleaned and properly formatted) of the customfield&lt;br /&gt;
&lt;br /&gt;
                        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
                        $pdf-&amp;gt;MultiCell(0,3, $translatedname.&#039;: &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
                        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3/ Call the _pagecustomfields function at the right place (generally, after _pagefoot and just before $pdf-&amp;gt;Close()):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Pied de page&lt;br /&gt;
$this-&amp;gt;_pagefoot($pdf,$object,$outputlangs);&lt;br /&gt;
$pdf-&amp;gt;AliasNbPages();&lt;br /&gt;
&lt;br /&gt;
// CustomFields&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) { // if the customfields module is activated...&lt;br /&gt;
        // Add a page with the customfields&lt;br /&gt;
        $pdf-&amp;gt;AddPage();&lt;br /&gt;
        $this-&amp;gt;_pagecustomfields($pdf,$object,$outputlangs);&lt;br /&gt;
        $pagenb++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
$pdf-&amp;gt;Close(); // you should first search for this, you should only find one occurrence&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then go to the module&#039;s admin panel (eg: invoice admin panel) and enable the template you&#039;ve just created.&lt;br /&gt;
&lt;br /&gt;
You can now use it to test if your customfields work.&lt;br /&gt;
&lt;br /&gt;
= Conditions on fields selection =&lt;br /&gt;
&lt;br /&gt;
Sometimes you may want to show a DropdownBox or Constraint (or another multi-choice type), but you may want to show or hide choices based on conditions (eg: only if it&#039;s a prospect, only if this zone was selected, only if this category was selected, etc..).&lt;br /&gt;
&lt;br /&gt;
To know which is the best solution you should choose to implement your condition, you should first know in which category your condition falls in.&lt;br /&gt;
&lt;br /&gt;
== Conditions categories ==&lt;br /&gt;
&lt;br /&gt;
Conditions can be categorized into 4 types:&lt;br /&gt;
&lt;br /&gt;
* static condition: choices are restrained on a condition that never changes (eg: only show third-parties that are suppliers, or lowercase all data on sql insertion, or check that a field is above a certain number, etc.).&lt;br /&gt;
* semi-dynamic condition: choices that are constrained on a static table but based on the value of another field/customfield. This is also commonly called &amp;quot;Cascaded dropdown lists&amp;quot; or &amp;quot;Dynamically linked dropdown lists&amp;quot;.&lt;br /&gt;
* dynamic: everything is dynamic: choices are based on a subfield of a field of the current module, so that there&#039;s no material table and it&#039;s impossible to create a view (because you would need to create one view per id) (eg: show only the contacts attached to the third-party being accessed)&lt;br /&gt;
* timed condition: a condition that is based on time or recurrence (eg: at 5:00 AM delete old records, etc..)&lt;br /&gt;
&lt;br /&gt;
== Conditions solutions ==&lt;br /&gt;
&lt;br /&gt;
Now that you know what kind of condition you want to set, you can choose one of the following solutions depending on your preferences and knowledge.&lt;br /&gt;
&lt;br /&gt;
In any case, you can always use the &#039;&#039;&#039;CustomFields&#039;s overload functions&#039;&#039;&#039; to fit your needs for nearly every type of condition.&lt;br /&gt;
&lt;br /&gt;
=== Static condition ===&lt;br /&gt;
&lt;br /&gt;
* View: create a view on the table based on the condition, and then create a custom field constrained on this view (which is just like any table), eg: only show third-parties that are suppliers:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
CREATE VIEW llx_societe_supplier_view AS&lt;br /&gt;
SELECT *&lt;br /&gt;
FROM llx_societe&lt;br /&gt;
WHERE fournisseur=1;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
WARNING: foreign keys (and thus constraints) can&#039;t be used on views with MySQL, and materialized views neither exist, so you can&#039;t use this method with MySQL.&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
* SQL Transformations&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
=== Semi-dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade option is ideal in most cases, and if not enough, you can use the Cascade Custom option and Overloading Functions.&lt;br /&gt;
* View&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
&lt;br /&gt;
=== Dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade Custom and Overload functions&lt;br /&gt;
* Making your own module, which calls the CustomFields class&lt;br /&gt;
&lt;br /&gt;
=== Timed condition ===&lt;br /&gt;
&lt;br /&gt;
* SQL scheduled events&lt;br /&gt;
* Cron job&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
= Overloading functions =&lt;br /&gt;
&lt;br /&gt;
Here are a few concrete use cases of overloading functions to achieve various goals.&lt;br /&gt;
&lt;br /&gt;
== Dynamic list selection based on another one ==&lt;br /&gt;
&lt;br /&gt;
The goal is to show two custom fields on the Third-Party module: one which gives the zone (secteur) of the third-party (Isere, Alpes du sud, Haute-Savoie...) and the other which gives relative to the zone the list of all the ski resorts (station_a) inside the selected zone.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will show how to manually manage this kind of requirements using [[Module_CustomFields#Overloading_functions:_Adding_your_own_management_code_for_custom_fields|overloading functions]], but in latest CustomFields releases, the same solution can be achieved automatically using the [[Module_CustomFields#Cascade|Cascade option]].&lt;br /&gt;
&lt;br /&gt;
We have one table &#039;&#039;&#039;llx_k_station&#039;&#039;&#039; (rowid, station (char50), secteur(char50)) which contains the following: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1,les 2 alpes,isère&lt;br /&gt;
2,chamrousse,isère&lt;br /&gt;
3,alpe d&#039;huez,isère&lt;br /&gt;
4,vars,alpes du sud&lt;br /&gt;
5,risoul,alpes du sud&lt;br /&gt;
etc...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In CustomFields&#039;s admin panel, we create two custom fields:&lt;br /&gt;
&lt;br /&gt;
1- secteur: returns the list of all zones - type DropdownBox: enum(&#039;- Aucun&#039;,&#039;Alpes du Sud&#039;,&#039;export&#039;,&#039;Haute-Savoie&#039;,&#039;Isère&#039;, etc...)&lt;br /&gt;
&lt;br /&gt;
2- station_a: returns the list of all ski resorts - type constraint on &#039;&#039;&#039;llx_k_station&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Here is the code to put in customfields_fields_extend.lib.php that will allow to show only the ski resorts corresponding to the selected zone:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_editview_societe_station_a (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$parameters, &amp;amp;$action, &amp;amp;$user, &amp;amp;$idvar, &amp;amp;$rightok, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
  global $db; // allow to access database functions&lt;br /&gt;
  $sql=&amp;quot;SELECT llx_k_station.station, llx_k_station.rowid FROM llx_societe_customfields INNER JOIN llx_k_station ON llx_societe_customfields.Secteur = llx_k_station.secteur WHERE llx_societe_customfields.fk_societe=&amp;quot;.$object-&amp;gt;id;&lt;br /&gt;
$result=$db-&amp;gt;query($sql);&lt;br /&gt;
 &lt;br /&gt;
  if ($result) {&lt;br /&gt;
    $num = $db-&amp;gt;num_rows($result);&lt;br /&gt;
    $i = 0;&lt;br /&gt;
    $myarray = array();&lt;br /&gt;
    while ($i &amp;lt; $num)&lt;br /&gt;
    {&lt;br /&gt;
      $obj = $db-&amp;gt;fetch_object($result);&lt;br /&gt;
      $myarray []=array(&#039;id&#039;=&amp;gt;$obj-&amp;gt;rowid, &#039;value&#039;=&amp;gt;$obj-&amp;gt;station);&lt;br /&gt;
      $i++;&lt;br /&gt;
    }&lt;br /&gt;
    $value = $myarray; &lt;br /&gt;
    $db-&amp;gt;free($result);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to manub for giving this case.&lt;br /&gt;
&lt;br /&gt;
== Dynamic discount computation per products lines on propales ==&lt;br /&gt;
&lt;br /&gt;
The goal here is to compute the discount for a product line in propales based on a computation on three custom fields.&lt;br /&gt;
&lt;br /&gt;
First, create three custom fields for &amp;quot;Line commercial proposals&amp;quot;: vl, cpl and lc.&lt;br /&gt;
&lt;br /&gt;
Then add the following overloading function at the end of your customfields_fields_extend.lib.php file (read the comments for more informations):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_propaldet_vl (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
&lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $vl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;vl&#039;};&lt;br /&gt;
    $cpl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;cpl&#039;};&lt;br /&gt;
    $lc = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;lc&#039;};&lt;br /&gt;
&lt;br /&gt;
    // Compute the discount&lt;br /&gt;
    $remise_calculee = (100 - $vl) * (100 - $cpl) * (100 - $lc);&lt;br /&gt;
&lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $remise_calculee = price2num($remise_calculee);&lt;br /&gt;
&lt;br /&gt;
    // Assign the new discount into the PropalLine object&lt;br /&gt;
    $object-&amp;gt;remise_percent = $remise_calculee;&lt;br /&gt;
&lt;br /&gt;
    // Update the price (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
&lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
&lt;br /&gt;
    //$q = &amp;quot;UPDATE  &amp;quot;.MAIN_DB_PREFIX.&amp;quot;propaldet SET remise_percent=&amp;quot;.$remise_calculee.&amp;quot; WHERE  rowid=&amp;quot;.$object-&amp;gt;rowid;&lt;br /&gt;
    //$db-&amp;gt;query($q);&lt;br /&gt;
    &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;vl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;cpl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;lc&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Linking Dolibarr objects from two different modules =&lt;br /&gt;
&lt;br /&gt;
Dolibarr sometimes offers the possibility to link two different modules. Eg: when you convert an Intervention card into an Invoice.&lt;br /&gt;
&lt;br /&gt;
In this case, you might want in your templates to fetch the custom fields of the linked object. Eg: you generate a PDF from the Invoice, but you want to get the custom fields from the Intervention that was converted into the Invoice.&lt;br /&gt;
&lt;br /&gt;
The key here is to use the &#039;&#039;&#039;llx_element_element&#039;&#039;&#039; table or the &#039;&#039;&#039;llx_element_*&#039;&#039;&#039; tables (eg: llx_element_contact): this is a standard Dolibarr table that stores all links between objects of two different modules.&lt;br /&gt;
&lt;br /&gt;
Here is the structure:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
rowid | fk_source | sourcetype | fk_target | targettype&lt;br /&gt;
-------------------------------------------------------&lt;br /&gt;
1 | 2 | fichinter | 5 | facture&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can then use a php code like the following to populate your $object with customfields:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
// This will also create and returns us an instance of the CustomFields class, which we will use to manually query the llx_element_element table&lt;br /&gt;
$customfields = customfields_fill_object($object, null, $outputlangs, &#039;facture&#039;, true); // beautified values&lt;br /&gt;
&lt;br /&gt;
// Querying the llx_element_element table manually to get the link between the invoice and the linked intervention card (we want the id of the intervention card)&lt;br /&gt;
// fetchAny($columns, $table, $where=&#039;&#039;, $orderby=&#039;&#039;, $limitby=&#039;&#039;)&lt;br /&gt;
$eltrow = $customfields-&amp;gt;fetchAny(&#039;*&#039;, MAIN_DB_PREFIX.&#039;element_element&#039;, &#039;targettype=&amp;quot;&#039;.$object-&amp;gt;element.&#039;&amp;quot; and fk_target=&#039;.$object-&amp;gt;id.&#039; and sourcetype=&amp;quot;fichinter&amp;quot;&#039;);&lt;br /&gt;
&lt;br /&gt;
// Save the intervention&#039;s id&lt;br /&gt;
$sourceid = $eltrow[0][&#039;fk_source&#039;];&lt;br /&gt;
&lt;br /&gt;
// Crafting a dummy intervention object&lt;br /&gt;
$fromobject = new stdClass(); // Init an empty object&lt;br /&gt;
$fromobject-&amp;gt;id = $sourceid; // We need the id and..&lt;br /&gt;
$fromobject-&amp;gt;table_element = &#039;fichinter&#039;; // the table_element (name of the table)&lt;br /&gt;
&lt;br /&gt;
// We can then fill $object with the intervention custom fields (we store them in a sub property &#039;fichinter&#039; to avoid conflicts with invoice&#039;s custom fields)&lt;br /&gt;
customfields_fill_object($object, $fromobject, $outputlangs, &#039;fichinter&#039;, true);&lt;br /&gt;
&lt;br /&gt;
// Now you can access invoices custom fields using:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;facture-&amp;gt;cf_myfield);&lt;br /&gt;
// And access intervention custom fields with:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;fichinter-&amp;gt;cf_myfield);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to netassopro for the tip.&lt;br /&gt;
&lt;br /&gt;
= Modify the total global price with a coefficient (without tampering the discount) =&lt;br /&gt;
&lt;br /&gt;
This is more troublesome than modifying the total price per product/service line, because the total global price of an object (let&#039;s say an invoice, but this also works for any other object) is always recomputed by summing the prices and quantity of each product/service line. This means that although there&#039;s an entry &amp;quot;total_ttc&amp;quot; in the database to store the invoice&#039;s total price, this price is in fact always recomputed, it&#039;s never used as-is. Thus, it&#039;s useless to change only the database record: we need to inject some code to change how the total price is computed.&lt;br /&gt;
&lt;br /&gt;
This could be done using an overloading function or a trigger, but unluckily, Dolibarr doesn&#039;t call a trigger nor a hook when recomputing the price. We thus need to edit core files to do what we want.&lt;br /&gt;
&lt;br /&gt;
However, the bright side is that we can cleverly edit just two functions from one core file: update_price() and getMarginInfos() from /htdocs/core/class/commonobject.class.php , and everything should run smoothly. Beware that if you update Dolibarr, you will need to redo these two modifications in the code.&lt;br /&gt;
&lt;br /&gt;
First, you need to create a new custom field called &amp;quot;global_coefficient&amp;quot; in Invoices (not Invoices Lines!). Then you can continue to the subchapters below.&lt;br /&gt;
&lt;br /&gt;
Also note that this global coefficient is also compatible with per line coefficients (as described in the next chapter), thus you can use a global coefficient and per line coefficients at the same time. You can also use multiple global coefficient if you wish.&lt;br /&gt;
&lt;br /&gt;
Another note: if you have a grid of discounts depending on quantity or price or whatever parameter, you can program a precomputation code using a Custom Ajax function (this way, your discounts will be automatically computed, but then you can still manually enter them for particular cases since the custom field is editable).&lt;br /&gt;
&lt;br /&gt;
== Implement the change of global price in most of Dolibarr (including remaining debt to pay and PDF and ODT) ==&lt;br /&gt;
&lt;br /&gt;
The change we will implement here will reflect the new total price with the global coefficient in most of Dolibarr&#039;s code, since we will here change the main function that is usually called to update and get the total price.&lt;br /&gt;
&lt;br /&gt;
Open /htdocs/core/class/commonobject.class.php and look for the function update_price().&lt;br /&gt;
&lt;br /&gt;
You should see the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function update_price($exclspec=0,$roundingadjust=&#039;none&#039;,$nodatabaseupdate=0,$seller=&#039;&#039;)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just below the include_once line, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;;&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/conf/conf_customfields_func.lib.php&#039;;&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $this-&amp;gt;table_element);&lt;br /&gt;
// Get object&#039;s id (either stored in property &amp;quot;id&amp;quot; or &amp;quot;rowid&amp;quot;)&lt;br /&gt;
$this_id = (!empty($this-&amp;gt;id)) ? $this-&amp;gt;id : $this-&amp;gt;rowid;&lt;br /&gt;
// Load custom fields records&lt;br /&gt;
$cf_parent_record = $customfields-&amp;gt;fetch($this_id);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will load the global_coefficient custom field (and also any other custom field you have defined for invoices).&lt;br /&gt;
&lt;br /&gt;
Now scroll below, and you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
while ($i &amp;lt; $num)&lt;br /&gt;
{&lt;br /&gt;
    $obj = $this-&amp;gt;db-&amp;gt;fetch_object($resql);&lt;br /&gt;
&lt;br /&gt;
    ... (lots of code)&lt;br /&gt;
&lt;br /&gt;
    i++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Add revenue stamp to total&lt;br /&gt;
$this-&amp;gt;total_ttc       += isset($this-&amp;gt;revenuestamp)?$this-&amp;gt;revenuestamp:0;&lt;br /&gt;
&lt;br /&gt;
$this-&amp;gt;db-&amp;gt;free($resql);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is the end of the loop that is summing each product/service line price to compute the total price. Just after this loop, you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Now update global field total_ht, total_ttc and tva&lt;br /&gt;
$fieldht=&#039;total_ht&#039;;&lt;br /&gt;
$fieldtva=&#039;tva&#039;;&lt;br /&gt;
$fieldlocaltax1=&#039;localtax1&#039;;&lt;br /&gt;
$fieldlocaltax2=&#039;localtax2&#039;;&lt;br /&gt;
$fieldttc=&#039;total_ttc&#039;;&lt;br /&gt;
// Specific code for backward compatibility with old field names&lt;br /&gt;
if ($this-&amp;gt;element == &#039;facture&#039; || $this-&amp;gt;element == &#039;facturerec&#039;)             $fieldht=&#039;total&#039;;&lt;br /&gt;
if ($this-&amp;gt;element == &#039;facture_fourn&#039; || $this-&amp;gt;element == &#039;invoice_supplier&#039;) $fieldtva=&#039;total_tva&#039;;&lt;br /&gt;
if ($this-&amp;gt;element == &#039;propal&#039;)                                                $fieldttc=&#039;total&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Between those two blocks of code (so just above // Now update global field total_ht, total_ttc and tva), you can place the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
if (!empty($cf_parent_record-&amp;gt;global_coefficient)) { // avoid applying the coefficient if empty (this will return a 0 price)&lt;br /&gt;
    $this-&amp;gt;total_ht = $this-&amp;gt;total_ht * $cf_parent_record-&amp;gt;global_coefficient;&lt;br /&gt;
    $this-&amp;gt;total_tva = $this-&amp;gt;total_tva * $cf_parent_record-&amp;gt;global_coefficient;&lt;br /&gt;
    $this-&amp;gt;total_ttc = $this-&amp;gt;total_ttc * $cf_parent_record-&amp;gt;global_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Done, now try to add/edit a product/service line, and the total price should be affected by the global coefficient.&lt;br /&gt;
&lt;br /&gt;
== Implementing the change of price in margin module ==&lt;br /&gt;
&lt;br /&gt;
If you enabled the margin module, you should see that the coefficient is not applied there, but it&#039;s easy to fix that.&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/class/commonobject.class.php and find the function getMarginInfos(), then inside find the following lines:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach($this-&amp;gt;lines as $line) {&lt;br /&gt;
    if (empty($line-&amp;gt;pa_ht) &amp;amp;&amp;amp; isset($line-&amp;gt;fk_fournprice) &amp;amp;&amp;amp; !$force_price) {&lt;br /&gt;
        $product = new ProductFournisseur($this-&amp;gt;db);&lt;br /&gt;
        if ($product-&amp;gt;fetch_product_fournisseur_price($line-&amp;gt;fk_fournprice))&lt;br /&gt;
            $line-&amp;gt;pa_ht = $product-&amp;gt;fourn_unitprice * (1 - $product-&amp;gt;fourn_remise_percent / 100);&lt;br /&gt;
        if (isset($conf-&amp;gt;global-&amp;gt;MARGIN_TYPE) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;MARGIN_TYPE == &amp;quot;2&amp;quot; &amp;amp;&amp;amp; $product-&amp;gt;fourn_unitcharges &amp;gt; 0)&lt;br /&gt;
            $line-&amp;gt;pa_ht += $product-&amp;gt;fourn_unitcharges;&lt;br /&gt;
    }&lt;br /&gt;
    // si prix d&#039;achat non renseigné et devrait l&#039;être, alors prix achat = prix vente&lt;br /&gt;
    if ((!isset($line-&amp;gt;pa_ht) || $line-&amp;gt;pa_ht == 0) &amp;amp;&amp;amp; $line-&amp;gt;subprice &amp;gt; 0 &amp;amp;&amp;amp; (isset($conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull == 1)) {&lt;br /&gt;
        $line-&amp;gt;pa_ht = $line-&amp;gt;subprice * (1 - ($line-&amp;gt;remise_percent / 100));&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the lines are filled with margin infos. The price has just been fetched here, and we will now modify it with the coefficient before the price gets used for margin computation.&lt;br /&gt;
&lt;br /&gt;
To optimize things up, we will load the custom fields before the loop (because they don&#039;t change between product/service lines, they are the custom fields of the parent object), and then apply the global_coefficient inside the loop.&lt;br /&gt;
&lt;br /&gt;
Just above the code we found (just before the for loop), paste the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
// Get object&#039;s id (either stored in property &amp;quot;id&amp;quot; or &amp;quot;rowid&amp;quot;)&lt;br /&gt;
$this_id = (!empty($this-&amp;gt;id)) ? $this-&amp;gt;id : $this-&amp;gt;rowid;&lt;br /&gt;
// Load custom fields records&lt;br /&gt;
$customfields = customfields_fill_object($this);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And now, just below the code we found, inside the loop (and just above the comment: &amp;quot;// calcul des marges&amp;quot;), paste the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
// Apply the coefficient&lt;br /&gt;
if (!empty($this-&amp;gt;customfields-&amp;gt;cf_global_coefficient)) {&lt;br /&gt;
    $line-&amp;gt;pa_ht = $line-&amp;gt;pa_ht * $this-&amp;gt;customfields-&amp;gt;cf_global_coefficient;&lt;br /&gt;
    $line-&amp;gt;subprice = $line-&amp;gt;subprice * $this-&amp;gt;customfields-&amp;gt;cf_global_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that here we cheated a bit: we do not compute the coefficient on the global total price, but rather per product/service line price. This is done on purpose, because mathematically this should produce no difference, and it also allows Dolibarr to continue peacefully with its margin calculations (else we would have to adapt all those computations, which would imply a lot more of code changes!).&lt;br /&gt;
&lt;br /&gt;
That&#039;s all, the price should be reflected in the margin. Just refresh the page to reflect the change.&lt;br /&gt;
&lt;br /&gt;
== Refresh the price instantly ==&lt;br /&gt;
&lt;br /&gt;
Maybe you noticed one glitch: when you modify the global_coefficient, the global total price isn&#039;t updated until you add/edit a product/service line. This is because the update_price() function is only called when we add/edit a product/service line.&lt;br /&gt;
&lt;br /&gt;
To fix that, we need now to force the refreshing of price. To do that, we will use an &amp;quot;aftersave&amp;quot; overloading function, which will execute commands after our custom field is saved.&lt;br /&gt;
&lt;br /&gt;
Go to the folder /htdocs/customfields/fields and rename the file customfields_fields_extend.lib.php.example into customfields_fields_extend.lib.php .&lt;br /&gt;
&lt;br /&gt;
Now, open it in your favourite text editor, and add the following function :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_aftersave_facture_global_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value, $fields) {&lt;br /&gt;
    // Force refreshing total price&lt;br /&gt;
    $object-&amp;gt;parent_object-&amp;gt;update_price(0, &#039;auto&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s all. Now, when you edit your global_coefficient, this will force refresh the total price.&lt;br /&gt;
&lt;br /&gt;
= Modify the total price PER product/service line with a coefficient (without tampering the discount) =&lt;br /&gt;
&lt;br /&gt;
Here we will go through the creation and management of a custom field called &#039;&#039;&#039;coefficient&#039;&#039;&#039; that will change the total price of each product by modifying it by the coefficient.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will not create a coefficient per product but rather per product&#039;s line. Thus if you want to create a coefficient that never change between invoices but only depending on the product and stays static, please refer to the next chapter below about creating a custom tax per product.&lt;br /&gt;
&lt;br /&gt;
The preliminary and necessary step is to first create a custom field that will be used to modify the total price. We will create one for Client Invoice Lines.&lt;br /&gt;
&lt;br /&gt;
Go to the CustomFields administration panel, then into the Client Invoice Lines tab, then click on the button New Field, name the field &#039;&#039;&#039;coefficient&#039;&#039;&#039;, and set the type to &#039;&#039;&#039;Double&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
When you go to client invoices in Dolibarr, you should then see something similar to this:&lt;br /&gt;
[[File:Cfe1.jpg]]&lt;br /&gt;
You can see that our coefficient field was successfully created.&lt;br /&gt;
&lt;br /&gt;
Then here are two alternative ways of implementing the change of the total price of each product relativery to this coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change total price in database ==&lt;br /&gt;
&lt;br /&gt;
Here we will use a &amp;quot;save&amp;quot; overloading function to change the total price directly in the database, thus we won&#039;t have to modify anything else (eg: nor PDF templating nor ODT) since the total price will be already updated in the database.&lt;br /&gt;
&lt;br /&gt;
Warning: this method will work only with CustomFields =&amp;gt; 3.2.23 since the &amp;quot;save&amp;quot; overloading function did not work for products lines before.&lt;br /&gt;
&lt;br /&gt;
Just add the following overloading function in your customfields_fields_extend.lib.php file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_facturedet_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
 &lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $coefficient = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;coefficient&#039;}; // you could also use $coefficient = $value;&lt;br /&gt;
 &lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $coefficient = price2num($coefficient);&lt;br /&gt;
 &lt;br /&gt;
    // Get the original subprices (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
 &lt;br /&gt;
    // Update the subprices with the coefficient&lt;br /&gt;
    $object-&amp;gt;total_ht = $object-&amp;gt;total_ht * $coefficient; // compute no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva = $object-&amp;gt;total_tva * $coefficient; // compute tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc * $coefficient; // compute tax included price&lt;br /&gt;
 &lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(&#039;&#039;,true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
 &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;coefficient&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should show the correct total price on the Dolibarr&#039;s interface without modifying the unit subprice (P.U. Unit):&lt;br /&gt;
&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
You have nothing to edit in your PDF or ODT template, since the new subprice with applied coefficient will be automatically printed instead of the old one without coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change printed total price per product without tampering the subprice in the database ==&lt;br /&gt;
&lt;br /&gt;
Here is a way to change the printed total price (the one shown on screen and in PDF/ODT) but without changing the total price in the database.&lt;br /&gt;
&lt;br /&gt;
The following sub-chapters are all independent, thus you can only follow the ones that you require, eg: if you only need to change the price in your PDF documents, just create a custom field then jump to the sub-chapter to &#039;&#039;&#039;Implement the change of price in PDF&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in ODT templates ===&lt;br /&gt;
&lt;br /&gt;
There&#039;s no eval implementation currently inside ODT templates, but there is a kind of hook implementation, called [[Create_an_ODT_document_template#Other_personalized_tags|substitution functions]].&lt;br /&gt;
&lt;br /&gt;
You can either create your own module, then create your own substitution function, but for the sake of simplicity in this tutorial, we will directly use the CustomFields substitution function. However, please remember that you will have to redo your changes everytime you will update the CustomFields module, so if you don&#039;t want to do that it would be better if you create your own module and substitution function.&lt;br /&gt;
&lt;br /&gt;
Open the file &#039;&#039;&#039;htdocs/customfields/core/substitutions/functions_customfields.lib.php&#039;&#039;&#039; and go to the function &#039;&#039;&#039;customfields_completesubstitutionarray_lines&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
At the very end of this function, but inside the customfields check:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) {&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
But just before the ending bracket, you should add the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($object-&amp;gt;table_element_line === &#039;facturedet&#039;) { // First check that we are in the right module, we don&#039;t want to use coefficient for other modules if we didn&#039;t define the custom field there&lt;br /&gt;
    $substitutionarray[&#039;line_price_full&#039;] = $substitutionarray[&#039;line_price_ttc&#039;] * $substitutionarray[&#039;cf_coefficient&#039;]; // multiply the total price with our coefficient&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code will add a new tag &#039;&#039;&#039;{line_price_full}&#039;&#039;&#039; that you can then use inside your ODT templates.&lt;br /&gt;
&lt;br /&gt;
Just place this tag in a table in your ODT document, between the table tags ( [!-- BEGIN row.lines --] ... [!-- END row.lines --] ), and this will print your full price.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in PDF templates ===&lt;br /&gt;
&lt;br /&gt;
First, we will create our own PDF template by copying an existing template, and then we will edit it to change the price of items.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Creating your own PDF template by copying an existing template&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Go into the following folder: &#039;&#039;&#039;htdocs/core/modules/facture/doc/&#039;&#039;&#039;, then copy the file &#039;&#039;&#039;pdf_crabe.modules.php&#039;&#039;&#039; and name the new one &#039;&#039;&#039;pdf_crabecoeff.modules.php&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Then open it in your favourite editor, and change the following lines:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures // change pdf_crabe into pdf_crabecoeff&lt;br /&gt;
{&lt;br /&gt;
...&lt;br /&gt;
    function __construct($db)&lt;br /&gt;
    {&lt;br /&gt;
        ...&lt;br /&gt;
	$this-&amp;gt;name = &amp;quot;crabe&amp;quot;; // change &amp;quot;crabe&amp;quot; into &amp;quot;crabecoeff&amp;quot;&lt;br /&gt;
        ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you have your own PDF template!&lt;br /&gt;
&lt;br /&gt;
Now you should &#039;&#039;&#039;enable your PDF template&#039;&#039;&#039; before doing anything else, by going into the Modules Administration panel, and into the Invoice admin page, then click on the ON button at the right of the &#039;&#039;&#039;crabecoeff&#039;&#039;&#039; entry to enable the template.&lt;br /&gt;
&lt;br /&gt;
Now, let&#039;s proceed onto modifying the prices.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Changing the prices automatically in your PDF&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
You can now do pretty much anything you want in your PDF template, but I will give you a mean to automatically change the prices of every products with our coefficient, as well as the total price, without editing too much the template.&lt;br /&gt;
&lt;br /&gt;
First, try to find the following code of line (should be inside the write_file() method):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf=pdf_getInstance($this-&amp;gt;format);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, just copy/paste the following code &#039;&#039;&#039;below&#039;&#039;&#039; pdf_getInstance():&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$this-&amp;gt;customfields = customfields_fill_object($object, null, $outputlangs, null, true); // beautified values&lt;br /&gt;
$this-&amp;gt;customfields_raw = customfields_fill_object($object, null, $outputlangs, &#039;raw&#039;, null); // raw values&lt;br /&gt;
$this-&amp;gt;customfields_lines = customfields_fill_object_lines($object, null, $outputlangs, null, true); // product lines&#039; values&lt;br /&gt;
&lt;br /&gt;
// Resetting every $object price because we will compute them anew&lt;br /&gt;
$object-&amp;gt;total_ht = 0;&lt;br /&gt;
$object-&amp;gt;total_tva = 0;&lt;br /&gt;
$object-&amp;gt;total_ttc = 0;&lt;br /&gt;
// For each line of product/service, we will recompute all the prices&lt;br /&gt;
foreach($object-&amp;gt;lines as $line) {&lt;br /&gt;
    // Get the coefficient custom field&#039;s value for the current line&lt;br /&gt;
    $coeff = $object-&amp;gt;customfields-&amp;gt;lines-&amp;gt;{$line-&amp;gt;rowid}-&amp;gt;cf_coefficient;&lt;br /&gt;
    // Recompute the price of this product/service line&lt;br /&gt;
    $line-&amp;gt;total_ht = $line-&amp;gt;total_ht*$coeff; // compute no tax price&lt;br /&gt;
    $line-&amp;gt;total_tva = $line-&amp;gt;total_tva*$coeff; // compute tax amount&lt;br /&gt;
    $line-&amp;gt;total_ttc = $line-&amp;gt;total_ttc*$coeff; // compute tax included price&lt;br /&gt;
    // Do the sum of all new products/services prices and tva (will be incremented with every product/service line)&lt;br /&gt;
    $object-&amp;gt;total_ht += $line-&amp;gt;total_ht; // global no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva += $line-&amp;gt;total_tva; // global vat/tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc += $line-&amp;gt;total_ttc; // global  tax included price&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you should try to generate your PDF document, and you should get something like this:&lt;br /&gt;
[[File:Cfe3.jpg]]&lt;br /&gt;
&lt;br /&gt;
Note: printing the coefficient as a column in your PDF document is also possible but will not be covered here because PDF templates will vary for every persons depending on your needs.&lt;br /&gt;
For guidelines about how to do this, you can read the documentation on [[Create_a_PDF_document_template|how to create a PDF template]] and [[Module_CustomFields#Implementing_in_PDF_templates|how to use custom fields in PDF templates]].&lt;br /&gt;
&lt;br /&gt;
Hint: you should be looking inside the following conditional block, inside the write_file() method:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
{&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of total price in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
Unluckily there is not (yet) any hooking context to modify how lines of products/services are printed, thus the only thing you can do is directly edit the &#039;&#039;&#039;template file&#039;&#039;&#039; and manually put inside your code to load your custom field and print it the way you want using HTML formatting, for our example:&lt;br /&gt;
&lt;br /&gt;
* For Dolibarr 3.3 and above, edit the file &#039;&#039;&#039;htdocs/core/tpl/objectline_view.tpl.php&#039;&#039;&#039;&lt;br /&gt;
* For Dolibarr 3.1 and 3.2, edit the file &#039;&#039;&#039;htdocs/core/tpl/freeproductline_view.tpl.php&#039;&#039;&#039; AND &#039;&#039;&#039;predefinedproductline_view.tpl.php&#039;&#039;&#039; (apply the same changes to both files)&lt;br /&gt;
&lt;br /&gt;
In the template file you opened, find the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And replace it by:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $object-&amp;gt;table_element_line; // you can use $object-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
&lt;br /&gt;
// Modifying the HT price and printing it on the Dolibarr&#039;s datasheet interface&lt;br /&gt;
// Applying a multiplying/dividing coefficient to an HT price will give a mathematically correct TTC price since the taxes are also a multiplier&lt;br /&gt;
// However, if you want to apply other mathematical operations (like addition or substaction), you should use the total price with $line-&amp;gt;total_ttc&lt;br /&gt;
if (!empty($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
    echo price($line-&amp;gt;total_ht * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient);&lt;br /&gt;
} else {&lt;br /&gt;
    echo price($line-&amp;gt;total_ht);&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should produce something like this:&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Technical note: here we are using customfields_fill_object() instead of customfields_fill_object_lines() (the latter being specifically adapted to lines and thus should theoretically be used here) for performance reasons because the template file we are editing is called in a loop, and customfields_fill_object_lines() should be called outside a loop (because it already does the job that is done by the loop), so here we &#039;tricks&#039; the customfields_fill_object() function to avoid this loop by submitting &#039;facturedet&#039; as a module by itself (when in fact it&#039;s a product lines table, but it works the same way nevertheless). This is a good demonstration of the flexibility of the CustomFields functions and methods.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the coefficient column in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
It&#039;s also possible to show the coefficient in a new column on the Dolibarr interface.&lt;br /&gt;
&lt;br /&gt;
To do that, you need to do two things:&lt;br /&gt;
&lt;br /&gt;
1- Add the column title &amp;quot;Coeff.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
2- Add the generator that will input the coefficient value for each line.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Add the column title &amp;quot;Coeff.&amp;quot;&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
Open in your favourite editor the file /htdocs/core/class/commonobject.class.php and inside the function printObjectLines (note the plural s), find the following lines of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Total HT&lt;br /&gt;
print &#039;&amp;lt;td align=&amp;quot;right&amp;quot; width=&amp;quot;50&amp;quot;&amp;gt;&#039;.$langs-&amp;gt;trans(&#039;TotalHTShort&#039;).&#039;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is what prints the Total HT price column. We will place the Coeff column just before.&lt;br /&gt;
&lt;br /&gt;
So, just above this code, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
dol_include_once(&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element_line);&lt;br /&gt;
$coeff_field = $customfields-&amp;gt;fetchFieldStruct(&#039;coefficient&#039;);&lt;br /&gt;
if (!empty($coeff_field)) {&lt;br /&gt;
    print &#039;&amp;lt;td align=&amp;quot;right&amp;quot; width=&amp;quot;50&amp;quot;&amp;gt;&#039;.$langs-&amp;gt;trans(&#039;Coeff.&#039;).&#039;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will take care of showing the Coeff. column, but only for modules where you created such a custom field (because the file we modified is a common object for all modules in Dolibarr, not just invoices, thus this modification will gracefully skip this custom field if it does not exists for other modules).&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Add the generator&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/tpl/objectline_view.tpl.php and try to find the following block of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php if ($line-&amp;gt;special_code == 3)	{ ?&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo $langs-&amp;gt;trans(&#039;Option&#039;); ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } else { ?&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is what prints the total HT price value for each line.&lt;br /&gt;
&lt;br /&gt;
Just above of this block of code, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $object-&amp;gt;table_element_line; // you can use $this-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
if (isset($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
    &amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient; ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just like for the column title, this code will take care of generating the coefficient value only if the custom field is defined, because this file is also a generic template for all modules of Dolibarr.&lt;br /&gt;
&lt;br /&gt;
At this point, everything is done, you should see the following:&lt;br /&gt;
[[File:Cf_wiki_coeff_column.png]]&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in margin module ===&lt;br /&gt;
&lt;br /&gt;
If you enabled the margin module, you should see that the coefficient is not applied there, but it&#039;s easy to fix that.&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/class/commonobject.class.php and find the function getMarginInfos(), then inside find the following lines:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach($this-&amp;gt;lines as $line) {&lt;br /&gt;
    if (empty($line-&amp;gt;pa_ht) &amp;amp;&amp;amp; isset($line-&amp;gt;fk_fournprice) &amp;amp;&amp;amp; !$force_price) {&lt;br /&gt;
        $product = new ProductFournisseur($this-&amp;gt;db);&lt;br /&gt;
        if ($product-&amp;gt;fetch_product_fournisseur_price($line-&amp;gt;fk_fournprice))&lt;br /&gt;
            $line-&amp;gt;pa_ht = $product-&amp;gt;fourn_unitprice * (1 - $product-&amp;gt;fourn_remise_percent / 100);&lt;br /&gt;
        if (isset($conf-&amp;gt;global-&amp;gt;MARGIN_TYPE) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;MARGIN_TYPE == &amp;quot;2&amp;quot; &amp;amp;&amp;amp; $product-&amp;gt;fourn_unitcharges &amp;gt; 0)&lt;br /&gt;
            $line-&amp;gt;pa_ht += $product-&amp;gt;fourn_unitcharges;&lt;br /&gt;
    }&lt;br /&gt;
    // si prix d&#039;achat non renseigné et devrait l&#039;être, alors prix achat = prix vente&lt;br /&gt;
    if ((!isset($line-&amp;gt;pa_ht) || $line-&amp;gt;pa_ht == 0) &amp;amp;&amp;amp; $line-&amp;gt;subprice &amp;gt; 0 &amp;amp;&amp;amp; (isset($conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull == 1)) {&lt;br /&gt;
        $line-&amp;gt;pa_ht = $line-&amp;gt;subprice * (1 - ($line-&amp;gt;remise_percent / 100));&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the lines are filled with margin infos. The price has just been fetched here, and we will now modify it with the coefficient before the price gets used for margin computation.&lt;br /&gt;
&lt;br /&gt;
Just below the code we found, paste the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $line-&amp;gt;table_element;&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
if (!empty($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
    $line-&amp;gt;pa_ht = $line-&amp;gt;pa_ht * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient;&lt;br /&gt;
    $line-&amp;gt;subprice = $line-&amp;gt;subprice * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s all, the price should be reflected in the margin.&lt;br /&gt;
[[File:Cf_wiki_coeff_margin.png]]&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in remaining debt to pay ===&lt;br /&gt;
&lt;br /&gt;
Open /htdocs/core/class/commonobject.class.php and look for the function update_price().&lt;br /&gt;
&lt;br /&gt;
You should see the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function update_price($exclspec=0,$roundingadjust=&#039;none&#039;,$nodatabaseupdate=0,$seller=&#039;&#039;)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just below the include_once line, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;;&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/conf/conf_customfields_func.lib.php&#039;;&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $this-&amp;gt;table_element_line);&lt;br /&gt;
// Fetching line ids from parent object id&lt;br /&gt;
$line_ids = $customfields-&amp;gt;fetchAny(&#039;rowid&#039;, MAIN_DB_PREFIX.$this-&amp;gt;table_element_line, $this-&amp;gt;fk_element.&#039; = &#039;.$this-&amp;gt;id);&lt;br /&gt;
$line_ids = array_values_recursive(&#039;rowid&#039;, $line_ids);&lt;br /&gt;
// Load custom fields&lt;br /&gt;
$cf_records = $customfields-&amp;gt;fetch($line_ids);&lt;br /&gt;
// Reassociate the records with the line id (instead of the customfields record id)&lt;br /&gt;
$cf_line_cfrowid_to_rowid = array_reassociate(&#039;rowid&#039;, &#039;fk_&#039;.$this-&amp;gt;table_element_line, $cf_records);&lt;br /&gt;
$tmp = $cf_records;&lt;br /&gt;
$cf_records = array();&lt;br /&gt;
foreach ($tmp as $k=&amp;gt;$v) { $cf_records[$cf_line_cfrowid_to_rowid[$k]] = $tmp[$k]; }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will load the coefficient custom field (and also any other custom field you have defined for lines).&lt;br /&gt;
&lt;br /&gt;
Now scroll below, and you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
while ($i &amp;lt; $num)&lt;br /&gt;
{&lt;br /&gt;
    $obj = $this-&amp;gt;db-&amp;gt;fetch_object($resql);&lt;br /&gt;
&lt;br /&gt;
    // Note: There is no check on detail line and no check on total, if $forcedroundingmode = &#039;none&#039;&lt;br /&gt;
&lt;br /&gt;
    if ($forcedroundingmode == &#039;0&#039;)	// Check if data on line are consistent. This may solve lines that were not consistent because set with $forcedroundingmode=&#039;auto&#039;&lt;br /&gt;
    {&lt;br /&gt;
        $localtax_array=array($obj-&amp;gt;localtax1_type,$obj-&amp;gt;localtax1_tx,$obj-&amp;gt;localtax2_type,$obj-&amp;gt;localtax2_tx);&lt;br /&gt;
        $tmpcal=calcul_price_total($obj-&amp;gt;qty, $obj-&amp;gt;up, $obj-&amp;gt;remise_percent, $obj-&amp;gt;vatrate, $obj-&amp;gt;localtax1_tx, $obj-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $obj-&amp;gt;info_bits, $obj-&amp;gt;product_type, $seller, $localtax_array);&lt;br /&gt;
        $diff=price2num($tmpcal[1] - $obj-&amp;gt;total_tva, &#039;MT&#039;, 1);&lt;br /&gt;
        if ($diff)&lt;br /&gt;
        {&lt;br /&gt;
            $sqlfix=&amp;quot;UPDATE &amp;quot;.MAIN_DB_PREFIX.$this-&amp;gt;table_element_line.&amp;quot; SET &amp;quot;.$fieldtva.&amp;quot; = &amp;quot;.$tmpcal[1].&amp;quot;, total_ttc = &amp;quot;.$tmpcal[2].&amp;quot; WHERE rowid = &amp;quot;.$obj-&amp;gt;rowid;&lt;br /&gt;
            dol_syslog(&#039;We found unconsistent data into detailed line (difference of &#039;.$diff.&#039;) for line rowid = &#039;.$obj-&amp;gt;rowid.&amp;quot; (total vat of line calculated=&amp;quot;.$tmpcal[1].&amp;quot;, database=&amp;quot;.$obj-&amp;gt;total_tva.&amp;quot;). We fix the total_vat and total_ttc of line by running sqlfix = &amp;quot;.$sqlfix);&lt;br /&gt;
            $resqlfix=$this-&amp;gt;db-&amp;gt;query($sqlfix);&lt;br /&gt;
            if (! $resqlfix) dol_print_error($this-&amp;gt;db,&#039;Failed to update line&#039;);&lt;br /&gt;
            $obj-&amp;gt;total_tva = $tmpcal[1];&lt;br /&gt;
            $obj-&amp;gt;total_ttc = $tmpcal[2];&lt;br /&gt;
            //&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just after, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
$cf_coeff = $cf_records[$obj-&amp;gt;rowid]-&amp;gt;coefficient;&lt;br /&gt;
if (empty($cf_coeff) and $cf_coeff !== 0) $cf_coeff = 1; // by default, an empty coefficient = 1&lt;br /&gt;
$obj-&amp;gt;total_ht = $obj-&amp;gt;total_ht * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_tva = $obj-&amp;gt;total_tva * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_localtax1 = $obj-&amp;gt;total_localtax1 * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_localtax2 = $obj-&amp;gt;total_localtax2 * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_ttc = $obj-&amp;gt;total_ttc * $cf_coeff;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should do the trick. Now go to one of your invoices (or whatever module you are targetting), and try to add/edit a product/service line to force refresh the total price. You should then see this:&lt;br /&gt;
[[File:Cf_wiki_coeff_remaining_debt.png]]&lt;br /&gt;
&lt;br /&gt;
= Custom tax per product =&lt;br /&gt;
&lt;br /&gt;
This part will show you how to create a static custom tax per product. Static means that the tax will be stored per product, and not per product&#039;s line, thus you just have to setup the tax once per product and it will always be applied in every invoice you add this product.&lt;br /&gt;
&lt;br /&gt;
To do that, you will need the CustomFields Pro module + ProductsEasyAccess.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
1- Create a product&#039;s custom fields called &amp;quot;custom_tax&amp;quot;. Now, using ProductsEasyAccess, you can access this field wherever you wants, either to show it on the Dolibarr&#039;s interface (see previous chapter), or in a PDF template, which we will describe below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2- Modify your invoice&#039;s PDF template. You first need to load all the products&#039; fields &#039;&#039;before&#039;&#039; the loop over the products&#039; lines. To do this, search for a line like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// This should be placed above the &amp;quot;loop lines&amp;quot; below, add this:&lt;br /&gt;
&lt;br /&gt;
dol_include_once(&#039;/productseasyaccess/lib/productseasyaccess.lib.php&#039;);&lt;br /&gt;
fill_object_with_products_fields($object);&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines, search for this command&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  // Add this just below the for loop, this will ease your php manipulation after.&lt;br /&gt;
  // This will allow you to more easily access the products&#039; fields (this will store the id of each product inside $lineproductid).&lt;br /&gt;
  $line_product_id = $object-&amp;gt;lines[$i]-&amp;gt;fk_product;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4- You can now use your products&#039; fields in your PDF. To access the custom_tax field, just use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And to print it in your PDF:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell(0,3, $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax, 0, &#039;L&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5- Then, if you want to count the custom tax total and add it to your vat total, you can create a counter inside the for loop we cited in the first step:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_custom_tax = 0; // Initialize the custom tax total counter&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  $object-&amp;gt;total_custom_tax += $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then find inside the function **_tableau_tot**, find the following line:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And prepend the total modification like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc + $object-&amp;gt;total_custom_tax; // prepend this to add the custom tax to the total&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Done. This should show your custom tax per product and your total including all custom taxes.&lt;/div&gt;</summary>
		<author><name>Lrq3000</name></author>
	</entry>
	<entry>
		<id>https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33071</id>
		<title>Module CustomFields Cases</title>
		<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33071"/>
		<updated>2015-01-15T15:56:05Z</updated>

		<summary type="html">&lt;p&gt;Lrq3000: /* Implementing the change of price in margin module */ Optimized code a bit&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:CustomFields]]&lt;br /&gt;
{{TemplateDocDevEn}}&lt;br /&gt;
{{TemplateModEN}}&lt;br /&gt;
{{BasculeDevUserEn|&lt;br /&gt;
name=CustomFields|&lt;br /&gt;
num=8500|&lt;br /&gt;
devdoc=[[Module_CustomFields_Dev]]|&lt;br /&gt;
userdoc=[[Module_CustomFields]]|}}&lt;br /&gt;
This page lists a compilation of concrete, practical examples uses of [[Module_CustomFields|CustomFields]] and of its various features.&lt;br /&gt;
&lt;br /&gt;
If you had an experience with CustomFields, you can also add here your case as an example for others to get inspired.&lt;br /&gt;
&lt;br /&gt;
= How to make a generic PDF template listing all custom fields =&lt;br /&gt;
&lt;br /&gt;
To make a generic PDF template listing all custom fields, you just need to take an existing PDF template, then you need to follow these 3 steps:&lt;br /&gt;
&lt;br /&gt;
1/ Edit the class name (required, else it will conflict with the original template).&lt;br /&gt;
&lt;br /&gt;
Eg: if we take the crabe template for invoices, we change:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;crabe&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
into:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_customfields extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;customfields&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2/ Copy this generic function at the end of the PDF:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propale/order/etc... (CustomFields simpler functions will automatically adapt)&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs); // set default PDF font size&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
    // Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
    $customfields = customfields_fill_object($object, null, $outputlangs, null, true);&lt;br /&gt;
&lt;br /&gt;
    // Setting the starting position of the text cursor&lt;br /&gt;
    $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
    $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
    // Printing the customfields&lt;br /&gt;
    foreach ($object-&amp;gt;customfields as $label=&amp;gt;$value) { // $value is already formatted!&lt;br /&gt;
        // Get translated label&lt;br /&gt;
        $translatedlabel = $customfields-&amp;gt;findLabelPDF($label, $outputlangs); // translated label of the customfield (not translated by default in customfields_fill_object() because a field should always be accessible by a base name, whatever the translation is)&lt;br /&gt;
&lt;br /&gt;
        // PDF formatting, placement and printing&lt;br /&gt;
        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
        $pdf-&amp;gt;MultiCell(0,3, $translatedlabel.&#039; ($object-&amp;gt;customfields-&amp;gt;&#039;.$label.&#039;): &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternative function (with a bit less functionalities, but more controllable):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page (used to debug if CustomFields setup is correct)&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propal/product/whatever...&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs);&lt;br /&gt;
&lt;br /&gt;
            if (empty($object-&amp;gt;table_element) or empty($object-&amp;gt;id)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;Current \$object is not compatible with CustomFields, could not find table_element or id.&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
    $customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element);&lt;br /&gt;
&lt;br /&gt;
            // Fetching custom fields records&lt;br /&gt;
            $fields = $customfields-&amp;gt;fetch($object-&amp;gt;id);&lt;br /&gt;
&lt;br /&gt;
            if (!isset($fields)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;No custom field could be found for this object. Please check your configuration (did you create at least one customfield and set a value in the current datasheet?)&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            } else {&lt;br /&gt;
                // Setting the starting position of the text cursor&lt;br /&gt;
                $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
                $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
                // Printing the customfields&lt;br /&gt;
                foreach ($fields as $key=&amp;gt;$field) {&lt;br /&gt;
                        $translatedname = $customfields-&amp;gt;findLabelPDF($key, $outputlangs); // label of the customfield&lt;br /&gt;
                        $value = $customfields-&amp;gt;simpleprintFieldPDF($key, $field, $outputlangs); // value (cleaned and properly formatted) of the customfield&lt;br /&gt;
&lt;br /&gt;
                        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
                        $pdf-&amp;gt;MultiCell(0,3, $translatedname.&#039;: &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
                        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3/ Call the _pagecustomfields function at the right place (generally, after _pagefoot and just before $pdf-&amp;gt;Close()):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Pied de page&lt;br /&gt;
$this-&amp;gt;_pagefoot($pdf,$object,$outputlangs);&lt;br /&gt;
$pdf-&amp;gt;AliasNbPages();&lt;br /&gt;
&lt;br /&gt;
// CustomFields&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) { // if the customfields module is activated...&lt;br /&gt;
        // Add a page with the customfields&lt;br /&gt;
        $pdf-&amp;gt;AddPage();&lt;br /&gt;
        $this-&amp;gt;_pagecustomfields($pdf,$object,$outputlangs);&lt;br /&gt;
        $pagenb++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
$pdf-&amp;gt;Close(); // you should first search for this, you should only find one occurrence&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then go to the module&#039;s admin panel (eg: invoice admin panel) and enable the template you&#039;ve just created.&lt;br /&gt;
&lt;br /&gt;
You can now use it to test if your customfields work.&lt;br /&gt;
&lt;br /&gt;
= Conditions on fields selection =&lt;br /&gt;
&lt;br /&gt;
Sometimes you may want to show a DropdownBox or Constraint (or another multi-choice type), but you may want to show or hide choices based on conditions (eg: only if it&#039;s a prospect, only if this zone was selected, only if this category was selected, etc..).&lt;br /&gt;
&lt;br /&gt;
To know which is the best solution you should choose to implement your condition, you should first know in which category your condition falls in.&lt;br /&gt;
&lt;br /&gt;
== Conditions categories ==&lt;br /&gt;
&lt;br /&gt;
Conditions can be categorized into 4 types:&lt;br /&gt;
&lt;br /&gt;
* static condition: choices are restrained on a condition that never changes (eg: only show third-parties that are suppliers, or lowercase all data on sql insertion, or check that a field is above a certain number, etc.).&lt;br /&gt;
* semi-dynamic condition: choices that are constrained on a static table but based on the value of another field/customfield. This is also commonly called &amp;quot;Cascaded dropdown lists&amp;quot; or &amp;quot;Dynamically linked dropdown lists&amp;quot;.&lt;br /&gt;
* dynamic: everything is dynamic: choices are based on a subfield of a field of the current module, so that there&#039;s no material table and it&#039;s impossible to create a view (because you would need to create one view per id) (eg: show only the contacts attached to the third-party being accessed)&lt;br /&gt;
* timed condition: a condition that is based on time or recurrence (eg: at 5:00 AM delete old records, etc..)&lt;br /&gt;
&lt;br /&gt;
== Conditions solutions ==&lt;br /&gt;
&lt;br /&gt;
Now that you know what kind of condition you want to set, you can choose one of the following solutions depending on your preferences and knowledge.&lt;br /&gt;
&lt;br /&gt;
In any case, you can always use the &#039;&#039;&#039;CustomFields&#039;s overload functions&#039;&#039;&#039; to fit your needs for nearly every type of condition.&lt;br /&gt;
&lt;br /&gt;
=== Static condition ===&lt;br /&gt;
&lt;br /&gt;
* View: create a view on the table based on the condition, and then create a custom field constrained on this view (which is just like any table), eg: only show third-parties that are suppliers:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
CREATE VIEW llx_societe_supplier_view AS&lt;br /&gt;
SELECT *&lt;br /&gt;
FROM llx_societe&lt;br /&gt;
WHERE fournisseur=1;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
WARNING: foreign keys (and thus constraints) can&#039;t be used on views with MySQL, and materialized views neither exist, so you can&#039;t use this method with MySQL.&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
* SQL Transformations&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
=== Semi-dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade option is ideal in most cases, and if not enough, you can use the Cascade Custom option and Overloading Functions.&lt;br /&gt;
* View&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
&lt;br /&gt;
=== Dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade Custom and Overload functions&lt;br /&gt;
* Making your own module, which calls the CustomFields class&lt;br /&gt;
&lt;br /&gt;
=== Timed condition ===&lt;br /&gt;
&lt;br /&gt;
* SQL scheduled events&lt;br /&gt;
* Cron job&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
= Overloading functions =&lt;br /&gt;
&lt;br /&gt;
Here are a few concrete use cases of overloading functions to achieve various goals.&lt;br /&gt;
&lt;br /&gt;
== Dynamic list selection based on another one ==&lt;br /&gt;
&lt;br /&gt;
The goal is to show two custom fields on the Third-Party module: one which gives the zone (secteur) of the third-party (Isere, Alpes du sud, Haute-Savoie...) and the other which gives relative to the zone the list of all the ski resorts (station_a) inside the selected zone.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will show how to manually manage this kind of requirements using [[Module_CustomFields#Overloading_functions:_Adding_your_own_management_code_for_custom_fields|overloading functions]], but in latest CustomFields releases, the same solution can be achieved automatically using the [[Module_CustomFields#Cascade|Cascade option]].&lt;br /&gt;
&lt;br /&gt;
We have one table &#039;&#039;&#039;llx_k_station&#039;&#039;&#039; (rowid, station (char50), secteur(char50)) which contains the following: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1,les 2 alpes,isère&lt;br /&gt;
2,chamrousse,isère&lt;br /&gt;
3,alpe d&#039;huez,isère&lt;br /&gt;
4,vars,alpes du sud&lt;br /&gt;
5,risoul,alpes du sud&lt;br /&gt;
etc...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In CustomFields&#039;s admin panel, we create two custom fields:&lt;br /&gt;
&lt;br /&gt;
1- secteur: returns the list of all zones - type DropdownBox: enum(&#039;- Aucun&#039;,&#039;Alpes du Sud&#039;,&#039;export&#039;,&#039;Haute-Savoie&#039;,&#039;Isère&#039;, etc...)&lt;br /&gt;
&lt;br /&gt;
2- station_a: returns the list of all ski resorts - type constraint on &#039;&#039;&#039;llx_k_station&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Here is the code to put in customfields_fields_extend.lib.php that will allow to show only the ski resorts corresponding to the selected zone:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_editview_societe_station_a (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$parameters, &amp;amp;$action, &amp;amp;$user, &amp;amp;$idvar, &amp;amp;$rightok, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
  global $db; // allow to access database functions&lt;br /&gt;
  $sql=&amp;quot;SELECT llx_k_station.station, llx_k_station.rowid FROM llx_societe_customfields INNER JOIN llx_k_station ON llx_societe_customfields.Secteur = llx_k_station.secteur WHERE llx_societe_customfields.fk_societe=&amp;quot;.$object-&amp;gt;id;&lt;br /&gt;
$result=$db-&amp;gt;query($sql);&lt;br /&gt;
 &lt;br /&gt;
  if ($result) {&lt;br /&gt;
    $num = $db-&amp;gt;num_rows($result);&lt;br /&gt;
    $i = 0;&lt;br /&gt;
    $myarray = array();&lt;br /&gt;
    while ($i &amp;lt; $num)&lt;br /&gt;
    {&lt;br /&gt;
      $obj = $db-&amp;gt;fetch_object($result);&lt;br /&gt;
      $myarray []=array(&#039;id&#039;=&amp;gt;$obj-&amp;gt;rowid, &#039;value&#039;=&amp;gt;$obj-&amp;gt;station);&lt;br /&gt;
      $i++;&lt;br /&gt;
    }&lt;br /&gt;
    $value = $myarray; &lt;br /&gt;
    $db-&amp;gt;free($result);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to manub for giving this case.&lt;br /&gt;
&lt;br /&gt;
== Dynamic discount computation per products lines on propales ==&lt;br /&gt;
&lt;br /&gt;
The goal here is to compute the discount for a product line in propales based on a computation on three custom fields.&lt;br /&gt;
&lt;br /&gt;
First, create three custom fields for &amp;quot;Line commercial proposals&amp;quot;: vl, cpl and lc.&lt;br /&gt;
&lt;br /&gt;
Then add the following overloading function at the end of your customfields_fields_extend.lib.php file (read the comments for more informations):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_propaldet_vl (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
&lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $vl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;vl&#039;};&lt;br /&gt;
    $cpl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;cpl&#039;};&lt;br /&gt;
    $lc = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;lc&#039;};&lt;br /&gt;
&lt;br /&gt;
    // Compute the discount&lt;br /&gt;
    $remise_calculee = (100 - $vl) * (100 - $cpl) * (100 - $lc);&lt;br /&gt;
&lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $remise_calculee = price2num($remise_calculee);&lt;br /&gt;
&lt;br /&gt;
    // Assign the new discount into the PropalLine object&lt;br /&gt;
    $object-&amp;gt;remise_percent = $remise_calculee;&lt;br /&gt;
&lt;br /&gt;
    // Update the price (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
&lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
&lt;br /&gt;
    //$q = &amp;quot;UPDATE  &amp;quot;.MAIN_DB_PREFIX.&amp;quot;propaldet SET remise_percent=&amp;quot;.$remise_calculee.&amp;quot; WHERE  rowid=&amp;quot;.$object-&amp;gt;rowid;&lt;br /&gt;
    //$db-&amp;gt;query($q);&lt;br /&gt;
    &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;vl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;cpl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;lc&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Linking Dolibarr objects from two different modules =&lt;br /&gt;
&lt;br /&gt;
Dolibarr sometimes offers the possibility to link two different modules. Eg: when you convert an Intervention card into an Invoice.&lt;br /&gt;
&lt;br /&gt;
In this case, you might want in your templates to fetch the custom fields of the linked object. Eg: you generate a PDF from the Invoice, but you want to get the custom fields from the Intervention that was converted into the Invoice.&lt;br /&gt;
&lt;br /&gt;
The key here is to use the &#039;&#039;&#039;llx_element_element&#039;&#039;&#039; table or the &#039;&#039;&#039;llx_element_*&#039;&#039;&#039; tables (eg: llx_element_contact): this is a standard Dolibarr table that stores all links between objects of two different modules.&lt;br /&gt;
&lt;br /&gt;
Here is the structure:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
rowid | fk_source | sourcetype | fk_target | targettype&lt;br /&gt;
-------------------------------------------------------&lt;br /&gt;
1 | 2 | fichinter | 5 | facture&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can then use a php code like the following to populate your $object with customfields:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
// This will also create and returns us an instance of the CustomFields class, which we will use to manually query the llx_element_element table&lt;br /&gt;
$customfields = customfields_fill_object($object, null, $outputlangs, &#039;facture&#039;, true); // beautified values&lt;br /&gt;
&lt;br /&gt;
// Querying the llx_element_element table manually to get the link between the invoice and the linked intervention card (we want the id of the intervention card)&lt;br /&gt;
// fetchAny($columns, $table, $where=&#039;&#039;, $orderby=&#039;&#039;, $limitby=&#039;&#039;)&lt;br /&gt;
$eltrow = $customfields-&amp;gt;fetchAny(&#039;*&#039;, MAIN_DB_PREFIX.&#039;element_element&#039;, &#039;targettype=&amp;quot;&#039;.$object-&amp;gt;element.&#039;&amp;quot; and fk_target=&#039;.$object-&amp;gt;id.&#039; and sourcetype=&amp;quot;fichinter&amp;quot;&#039;);&lt;br /&gt;
&lt;br /&gt;
// Save the intervention&#039;s id&lt;br /&gt;
$sourceid = $eltrow[0][&#039;fk_source&#039;];&lt;br /&gt;
&lt;br /&gt;
// Crafting a dummy intervention object&lt;br /&gt;
$fromobject = new stdClass(); // Init an empty object&lt;br /&gt;
$fromobject-&amp;gt;id = $sourceid; // We need the id and..&lt;br /&gt;
$fromobject-&amp;gt;table_element = &#039;fichinter&#039;; // the table_element (name of the table)&lt;br /&gt;
&lt;br /&gt;
// We can then fill $object with the intervention custom fields (we store them in a sub property &#039;fichinter&#039; to avoid conflicts with invoice&#039;s custom fields)&lt;br /&gt;
customfields_fill_object($object, $fromobject, $outputlangs, &#039;fichinter&#039;, true);&lt;br /&gt;
&lt;br /&gt;
// Now you can access invoices custom fields using:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;facture-&amp;gt;cf_myfield);&lt;br /&gt;
// And access intervention custom fields with:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;fichinter-&amp;gt;cf_myfield);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to netassopro for the tip.&lt;br /&gt;
&lt;br /&gt;
= Modify the total global price with a coefficient (without tampering the discount) =&lt;br /&gt;
&lt;br /&gt;
This is more troublesome than modifying the total price per product/service line, because the total global price of an object (let&#039;s say an invoice, but this also works for any other object) is always recomputed by summing the prices and quantity of each product/service line. This means that although there&#039;s an entry &amp;quot;total_ttc&amp;quot; in the database to store the invoice&#039;s total price, this price is in fact always recomputed, it&#039;s never used as-is. Thus, it&#039;s useless to change only the database record: we need to inject some code to change how the total price is computed.&lt;br /&gt;
&lt;br /&gt;
This could be done using an overloading function or a trigger, but unluckily, Dolibarr doesn&#039;t call a trigger nor a hook when recomputing the price. We thus need to edit core files to do what we want.&lt;br /&gt;
&lt;br /&gt;
However, the bright side is that we can cleverly edit just two functions from one core file: update_price() and getMarginInfos() from /htdocs/core/class/commonobject.class.php , and everything should run smoothly. Beware that if you update Dolibarr, you will need to redo these two modifications in the code.&lt;br /&gt;
&lt;br /&gt;
First, you need to create a new custom field called &amp;quot;global_coefficient&amp;quot; in Invoices (not Invoices Lines!). Then you can continue to the subchapters below.&lt;br /&gt;
&lt;br /&gt;
Also note that this global coefficient is also compatible with per line coefficients (as described in the next chapter), thus you can use a global coefficient and per line coefficients at the same time. You can also use multiple global coefficient if you wish.&lt;br /&gt;
&lt;br /&gt;
Another note: if you have a grid of discounts depending on quantity or price or whatever parameter, you can program a precomputation code using a Custom Ajax function (this way, your discounts will be automatically computed, but then you can still manually enter them for particular cases since the custom field is editable).&lt;br /&gt;
&lt;br /&gt;
== Implement the change of global price in most of Dolibarr (including remaining debt to pay and PDF and ODT) ==&lt;br /&gt;
&lt;br /&gt;
The change we will implement here will reflect the new total price with the global coefficient in most of Dolibarr&#039;s code, since we will here change the main function that is usually called to update and get the total price.&lt;br /&gt;
&lt;br /&gt;
Open /htdocs/core/class/commonobject.class.php and look for the function update_price().&lt;br /&gt;
&lt;br /&gt;
You should see the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function update_price($exclspec=0,$roundingadjust=&#039;none&#039;,$nodatabaseupdate=0,$seller=&#039;&#039;)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just below the include_once line, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;;&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/conf/conf_customfields_func.lib.php&#039;;&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $this-&amp;gt;table_element);&lt;br /&gt;
// Get object&#039;s id (either stored in property &amp;quot;id&amp;quot; or &amp;quot;rowid&amp;quot;)&lt;br /&gt;
$this_id = (!empty($this-&amp;gt;id)) ? $this-&amp;gt;id : $this-&amp;gt;rowid;&lt;br /&gt;
// Load custom fields records&lt;br /&gt;
$cf_parent_record = $customfields-&amp;gt;fetch($this_id);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will load the global_coefficient custom field (and also any other custom field you have defined for invoices).&lt;br /&gt;
&lt;br /&gt;
Now scroll below, and you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
while ($i &amp;lt; $num)&lt;br /&gt;
{&lt;br /&gt;
    $obj = $this-&amp;gt;db-&amp;gt;fetch_object($resql);&lt;br /&gt;
&lt;br /&gt;
    ... (lots of code)&lt;br /&gt;
&lt;br /&gt;
    i++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Add revenue stamp to total&lt;br /&gt;
$this-&amp;gt;total_ttc       += isset($this-&amp;gt;revenuestamp)?$this-&amp;gt;revenuestamp:0;&lt;br /&gt;
&lt;br /&gt;
$this-&amp;gt;db-&amp;gt;free($resql);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is the end of the loop that is summing each product/service line price to compute the total price. Just after this loop, you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Now update global field total_ht, total_ttc and tva&lt;br /&gt;
$fieldht=&#039;total_ht&#039;;&lt;br /&gt;
$fieldtva=&#039;tva&#039;;&lt;br /&gt;
$fieldlocaltax1=&#039;localtax1&#039;;&lt;br /&gt;
$fieldlocaltax2=&#039;localtax2&#039;;&lt;br /&gt;
$fieldttc=&#039;total_ttc&#039;;&lt;br /&gt;
// Specific code for backward compatibility with old field names&lt;br /&gt;
if ($this-&amp;gt;element == &#039;facture&#039; || $this-&amp;gt;element == &#039;facturerec&#039;)             $fieldht=&#039;total&#039;;&lt;br /&gt;
if ($this-&amp;gt;element == &#039;facture_fourn&#039; || $this-&amp;gt;element == &#039;invoice_supplier&#039;) $fieldtva=&#039;total_tva&#039;;&lt;br /&gt;
if ($this-&amp;gt;element == &#039;propal&#039;)                                                $fieldttc=&#039;total&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Between those two blocks of code (so just above // Now update global field total_ht, total_ttc and tva), you can place the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
if (!empty($cf_parent_record-&amp;gt;global_coefficient)) { // avoid applying the coefficient if empty (this will return a 0 price)&lt;br /&gt;
    $this-&amp;gt;total_ht = $this-&amp;gt;total_ht * $cf_parent_record-&amp;gt;global_coefficient;&lt;br /&gt;
    $this-&amp;gt;total_tva = $this-&amp;gt;total_tva * $cf_parent_record-&amp;gt;global_coefficient;&lt;br /&gt;
    $this-&amp;gt;total_ttc = $this-&amp;gt;total_ttc * $cf_parent_record-&amp;gt;global_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Done, now try to add/edit a product/service line, and the total price should be affected by the global coefficient.&lt;br /&gt;
&lt;br /&gt;
== Implementing the change of price in margin module ==&lt;br /&gt;
&lt;br /&gt;
If you enabled the margin module, you should see that the coefficient is not applied there, but it&#039;s easy to fix that.&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/class/commonobject.class.php and find the function getMarginInfos(), then inside find the following lines:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach($this-&amp;gt;lines as $line) {&lt;br /&gt;
    if (empty($line-&amp;gt;pa_ht) &amp;amp;&amp;amp; isset($line-&amp;gt;fk_fournprice) &amp;amp;&amp;amp; !$force_price) {&lt;br /&gt;
        $product = new ProductFournisseur($this-&amp;gt;db);&lt;br /&gt;
        if ($product-&amp;gt;fetch_product_fournisseur_price($line-&amp;gt;fk_fournprice))&lt;br /&gt;
            $line-&amp;gt;pa_ht = $product-&amp;gt;fourn_unitprice * (1 - $product-&amp;gt;fourn_remise_percent / 100);&lt;br /&gt;
        if (isset($conf-&amp;gt;global-&amp;gt;MARGIN_TYPE) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;MARGIN_TYPE == &amp;quot;2&amp;quot; &amp;amp;&amp;amp; $product-&amp;gt;fourn_unitcharges &amp;gt; 0)&lt;br /&gt;
            $line-&amp;gt;pa_ht += $product-&amp;gt;fourn_unitcharges;&lt;br /&gt;
    }&lt;br /&gt;
    // si prix d&#039;achat non renseigné et devrait l&#039;être, alors prix achat = prix vente&lt;br /&gt;
    if ((!isset($line-&amp;gt;pa_ht) || $line-&amp;gt;pa_ht == 0) &amp;amp;&amp;amp; $line-&amp;gt;subprice &amp;gt; 0 &amp;amp;&amp;amp; (isset($conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull == 1)) {&lt;br /&gt;
        $line-&amp;gt;pa_ht = $line-&amp;gt;subprice * (1 - ($line-&amp;gt;remise_percent / 100));&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the lines are filled with margin infos. The price has just been fetched here, and we will now modify it with the coefficient before the price gets used for margin computation.&lt;br /&gt;
&lt;br /&gt;
To optimize things up, we will load the custom fields before the loop (because they don&#039;t change between product/service lines, they are the custom fields of the parent object), and then apply the global_coefficient inside the loop.&lt;br /&gt;
&lt;br /&gt;
Just above the code we found (just before the for loop), paste the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
// Get object&#039;s id (either stored in property &amp;quot;id&amp;quot; or &amp;quot;rowid&amp;quot;)&lt;br /&gt;
$this_id = (!empty($this-&amp;gt;id)) ? $this-&amp;gt;id : $this-&amp;gt;rowid;&lt;br /&gt;
// Load custom fields records&lt;br /&gt;
$customfields = customfields_fill_object($this);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And now, just below the code we found, inside the loop (and just above the comment: &amp;quot;// calcul des marges&amp;quot;), paste the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
// Apply the coefficient&lt;br /&gt;
if (!empty($this-&amp;gt;customfields-&amp;gt;cf_global_coefficient)) {&lt;br /&gt;
    $line-&amp;gt;pa_ht = $line-&amp;gt;pa_ht * $this-&amp;gt;customfields-&amp;gt;cf_global_coefficient;&lt;br /&gt;
    $line-&amp;gt;subprice = $line-&amp;gt;subprice * $this-&amp;gt;customfields-&amp;gt;cf_global_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that here we cheated a bit: we do not compute the coefficient on the global total price, but rather per product/service line price. This is done on purpose, because mathematically this should produce no difference, and it also allows Dolibarr to continue peacefully with its margin calculations (else we would have to adapt all those computations, which would imply a lot more of code changes!).&lt;br /&gt;
&lt;br /&gt;
That&#039;s all, the price should be reflected in the margin. Just refresh the page to reflect the change.&lt;br /&gt;
&lt;br /&gt;
= Modify the total price PER product/service line with a coefficient (without tampering the discount) =&lt;br /&gt;
&lt;br /&gt;
Here we will go through the creation and management of a custom field called &#039;&#039;&#039;coefficient&#039;&#039;&#039; that will change the total price of each product by modifying it by the coefficient.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will not create a coefficient per product but rather per product&#039;s line. Thus if you want to create a coefficient that never change between invoices but only depending on the product and stays static, please refer to the next chapter below about creating a custom tax per product.&lt;br /&gt;
&lt;br /&gt;
The preliminary and necessary step is to first create a custom field that will be used to modify the total price. We will create one for Client Invoice Lines.&lt;br /&gt;
&lt;br /&gt;
Go to the CustomFields administration panel, then into the Client Invoice Lines tab, then click on the button New Field, name the field &#039;&#039;&#039;coefficient&#039;&#039;&#039;, and set the type to &#039;&#039;&#039;Double&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
When you go to client invoices in Dolibarr, you should then see something similar to this:&lt;br /&gt;
[[File:Cfe1.jpg]]&lt;br /&gt;
You can see that our coefficient field was successfully created.&lt;br /&gt;
&lt;br /&gt;
Then here are two alternative ways of implementing the change of the total price of each product relativery to this coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change total price in database ==&lt;br /&gt;
&lt;br /&gt;
Here we will use a &amp;quot;save&amp;quot; overloading function to change the total price directly in the database, thus we won&#039;t have to modify anything else (eg: nor PDF templating nor ODT) since the total price will be already updated in the database.&lt;br /&gt;
&lt;br /&gt;
Warning: this method will work only with CustomFields =&amp;gt; 3.2.23 since the &amp;quot;save&amp;quot; overloading function did not work for products lines before.&lt;br /&gt;
&lt;br /&gt;
Just add the following overloading function in your customfields_fields_extend.lib.php file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_facturedet_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
 &lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $coefficient = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;coefficient&#039;}; // you could also use $coefficient = $value;&lt;br /&gt;
 &lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $coefficient = price2num($coefficient);&lt;br /&gt;
 &lt;br /&gt;
    // Get the original subprices (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
 &lt;br /&gt;
    // Update the subprices with the coefficient&lt;br /&gt;
    $object-&amp;gt;total_ht = $object-&amp;gt;total_ht * $coefficient; // compute no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva = $object-&amp;gt;total_tva * $coefficient; // compute tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc * $coefficient; // compute tax included price&lt;br /&gt;
 &lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(&#039;&#039;,true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
 &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;coefficient&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should show the correct total price on the Dolibarr&#039;s interface without modifying the unit subprice (P.U. Unit):&lt;br /&gt;
&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
You have nothing to edit in your PDF or ODT template, since the new subprice with applied coefficient will be automatically printed instead of the old one without coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change printed total price per product without tampering the subprice in the database ==&lt;br /&gt;
&lt;br /&gt;
Here is a way to change the printed total price (the one shown on screen and in PDF/ODT) but without changing the total price in the database.&lt;br /&gt;
&lt;br /&gt;
The following sub-chapters are all independent, thus you can only follow the ones that you require, eg: if you only need to change the price in your PDF documents, just create a custom field then jump to the sub-chapter to &#039;&#039;&#039;Implement the change of price in PDF&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in ODT templates ===&lt;br /&gt;
&lt;br /&gt;
There&#039;s no eval implementation currently inside ODT templates, but there is a kind of hook implementation, called [[Create_an_ODT_document_template#Other_personalized_tags|substitution functions]].&lt;br /&gt;
&lt;br /&gt;
You can either create your own module, then create your own substitution function, but for the sake of simplicity in this tutorial, we will directly use the CustomFields substitution function. However, please remember that you will have to redo your changes everytime you will update the CustomFields module, so if you don&#039;t want to do that it would be better if you create your own module and substitution function.&lt;br /&gt;
&lt;br /&gt;
Open the file &#039;&#039;&#039;htdocs/customfields/core/substitutions/functions_customfields.lib.php&#039;&#039;&#039; and go to the function &#039;&#039;&#039;customfields_completesubstitutionarray_lines&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
At the very end of this function, but inside the customfields check:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) {&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
But just before the ending bracket, you should add the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($object-&amp;gt;table_element_line === &#039;facturedet&#039;) { // First check that we are in the right module, we don&#039;t want to use coefficient for other modules if we didn&#039;t define the custom field there&lt;br /&gt;
    $substitutionarray[&#039;line_price_full&#039;] = $substitutionarray[&#039;line_price_ttc&#039;] * $substitutionarray[&#039;cf_coefficient&#039;]; // multiply the total price with our coefficient&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code will add a new tag &#039;&#039;&#039;{line_price_full}&#039;&#039;&#039; that you can then use inside your ODT templates.&lt;br /&gt;
&lt;br /&gt;
Just place this tag in a table in your ODT document, between the table tags ( [!-- BEGIN row.lines --] ... [!-- END row.lines --] ), and this will print your full price.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in PDF templates ===&lt;br /&gt;
&lt;br /&gt;
First, we will create our own PDF template by copying an existing template, and then we will edit it to change the price of items.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Creating your own PDF template by copying an existing template&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Go into the following folder: &#039;&#039;&#039;htdocs/core/modules/facture/doc/&#039;&#039;&#039;, then copy the file &#039;&#039;&#039;pdf_crabe.modules.php&#039;&#039;&#039; and name the new one &#039;&#039;&#039;pdf_crabecoeff.modules.php&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Then open it in your favourite editor, and change the following lines:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures // change pdf_crabe into pdf_crabecoeff&lt;br /&gt;
{&lt;br /&gt;
...&lt;br /&gt;
    function __construct($db)&lt;br /&gt;
    {&lt;br /&gt;
        ...&lt;br /&gt;
	$this-&amp;gt;name = &amp;quot;crabe&amp;quot;; // change &amp;quot;crabe&amp;quot; into &amp;quot;crabecoeff&amp;quot;&lt;br /&gt;
        ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you have your own PDF template!&lt;br /&gt;
&lt;br /&gt;
Now you should &#039;&#039;&#039;enable your PDF template&#039;&#039;&#039; before doing anything else, by going into the Modules Administration panel, and into the Invoice admin page, then click on the ON button at the right of the &#039;&#039;&#039;crabecoeff&#039;&#039;&#039; entry to enable the template.&lt;br /&gt;
&lt;br /&gt;
Now, let&#039;s proceed onto modifying the prices.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Changing the prices automatically in your PDF&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
You can now do pretty much anything you want in your PDF template, but I will give you a mean to automatically change the prices of every products with our coefficient, as well as the total price, without editing too much the template.&lt;br /&gt;
&lt;br /&gt;
First, try to find the following code of line (should be inside the write_file() method):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf=pdf_getInstance($this-&amp;gt;format);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, just copy/paste the following code &#039;&#039;&#039;below&#039;&#039;&#039; pdf_getInstance():&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$this-&amp;gt;customfields = customfields_fill_object($object, null, $outputlangs, null, true); // beautified values&lt;br /&gt;
$this-&amp;gt;customfields_raw = customfields_fill_object($object, null, $outputlangs, &#039;raw&#039;, null); // raw values&lt;br /&gt;
$this-&amp;gt;customfields_lines = customfields_fill_object_lines($object, null, $outputlangs, null, true); // product lines&#039; values&lt;br /&gt;
&lt;br /&gt;
// Resetting every $object price because we will compute them anew&lt;br /&gt;
$object-&amp;gt;total_ht = 0;&lt;br /&gt;
$object-&amp;gt;total_tva = 0;&lt;br /&gt;
$object-&amp;gt;total_ttc = 0;&lt;br /&gt;
// For each line of product/service, we will recompute all the prices&lt;br /&gt;
foreach($object-&amp;gt;lines as $line) {&lt;br /&gt;
    // Get the coefficient custom field&#039;s value for the current line&lt;br /&gt;
    $coeff = $object-&amp;gt;customfields-&amp;gt;lines-&amp;gt;{$line-&amp;gt;rowid}-&amp;gt;cf_coefficient;&lt;br /&gt;
    // Recompute the price of this product/service line&lt;br /&gt;
    $line-&amp;gt;total_ht = $line-&amp;gt;total_ht*$coeff; // compute no tax price&lt;br /&gt;
    $line-&amp;gt;total_tva = $line-&amp;gt;total_tva*$coeff; // compute tax amount&lt;br /&gt;
    $line-&amp;gt;total_ttc = $line-&amp;gt;total_ttc*$coeff; // compute tax included price&lt;br /&gt;
    // Do the sum of all new products/services prices and tva (will be incremented with every product/service line)&lt;br /&gt;
    $object-&amp;gt;total_ht += $line-&amp;gt;total_ht; // global no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva += $line-&amp;gt;total_tva; // global vat/tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc += $line-&amp;gt;total_ttc; // global  tax included price&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you should try to generate your PDF document, and you should get something like this:&lt;br /&gt;
[[File:Cfe3.jpg]]&lt;br /&gt;
&lt;br /&gt;
Note: printing the coefficient as a column in your PDF document is also possible but will not be covered here because PDF templates will vary for every persons depending on your needs.&lt;br /&gt;
For guidelines about how to do this, you can read the documentation on [[Create_a_PDF_document_template|how to create a PDF template]] and [[Module_CustomFields#Implementing_in_PDF_templates|how to use custom fields in PDF templates]].&lt;br /&gt;
&lt;br /&gt;
Hint: you should be looking inside the following conditional block, inside the write_file() method:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
{&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of total price in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
Unluckily there is not (yet) any hooking context to modify how lines of products/services are printed, thus the only thing you can do is directly edit the &#039;&#039;&#039;template file&#039;&#039;&#039; and manually put inside your code to load your custom field and print it the way you want using HTML formatting, for our example:&lt;br /&gt;
&lt;br /&gt;
* For Dolibarr 3.3 and above, edit the file &#039;&#039;&#039;htdocs/core/tpl/objectline_view.tpl.php&#039;&#039;&#039;&lt;br /&gt;
* For Dolibarr 3.1 and 3.2, edit the file &#039;&#039;&#039;htdocs/core/tpl/freeproductline_view.tpl.php&#039;&#039;&#039; AND &#039;&#039;&#039;predefinedproductline_view.tpl.php&#039;&#039;&#039; (apply the same changes to both files)&lt;br /&gt;
&lt;br /&gt;
In the template file you opened, find the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And replace it by:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $object-&amp;gt;table_element_line; // you can use $object-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
&lt;br /&gt;
// Modifying the HT price and printing it on the Dolibarr&#039;s datasheet interface&lt;br /&gt;
// Applying a multiplying/dividing coefficient to an HT price will give a mathematically correct TTC price since the taxes are also a multiplier&lt;br /&gt;
// However, if you want to apply other mathematical operations (like addition or substaction), you should use the total price with $line-&amp;gt;total_ttc&lt;br /&gt;
if (!empty($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
    echo price($line-&amp;gt;total_ht * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient);&lt;br /&gt;
} else {&lt;br /&gt;
    echo price($line-&amp;gt;total_ht);&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should produce something like this:&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Technical note: here we are using customfields_fill_object() instead of customfields_fill_object_lines() (the latter being specifically adapted to lines and thus should theoretically be used here) for performance reasons because the template file we are editing is called in a loop, and customfields_fill_object_lines() should be called outside a loop (because it already does the job that is done by the loop), so here we &#039;tricks&#039; the customfields_fill_object() function to avoid this loop by submitting &#039;facturedet&#039; as a module by itself (when in fact it&#039;s a product lines table, but it works the same way nevertheless). This is a good demonstration of the flexibility of the CustomFields functions and methods.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the coefficient column in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
It&#039;s also possible to show the coefficient in a new column on the Dolibarr interface.&lt;br /&gt;
&lt;br /&gt;
To do that, you need to do two things:&lt;br /&gt;
&lt;br /&gt;
1- Add the column title &amp;quot;Coeff.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
2- Add the generator that will input the coefficient value for each line.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Add the column title &amp;quot;Coeff.&amp;quot;&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
Open in your favourite editor the file /htdocs/core/class/commonobject.class.php and inside the function printObjectLines (note the plural s), find the following lines of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Total HT&lt;br /&gt;
print &#039;&amp;lt;td align=&amp;quot;right&amp;quot; width=&amp;quot;50&amp;quot;&amp;gt;&#039;.$langs-&amp;gt;trans(&#039;TotalHTShort&#039;).&#039;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is what prints the Total HT price column. We will place the Coeff column just before.&lt;br /&gt;
&lt;br /&gt;
So, just above this code, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
dol_include_once(&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element_line);&lt;br /&gt;
$coeff_field = $customfields-&amp;gt;fetchFieldStruct(&#039;coefficient&#039;);&lt;br /&gt;
if (!empty($coeff_field)) {&lt;br /&gt;
    print &#039;&amp;lt;td align=&amp;quot;right&amp;quot; width=&amp;quot;50&amp;quot;&amp;gt;&#039;.$langs-&amp;gt;trans(&#039;Coeff.&#039;).&#039;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will take care of showing the Coeff. column, but only for modules where you created such a custom field (because the file we modified is a common object for all modules in Dolibarr, not just invoices, thus this modification will gracefully skip this custom field if it does not exists for other modules).&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Add the generator&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/tpl/objectline_view.tpl.php and try to find the following block of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php if ($line-&amp;gt;special_code == 3)	{ ?&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo $langs-&amp;gt;trans(&#039;Option&#039;); ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } else { ?&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is what prints the total HT price value for each line.&lt;br /&gt;
&lt;br /&gt;
Just above of this block of code, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $object-&amp;gt;table_element_line; // you can use $this-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
if (isset($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
    &amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient; ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just like for the column title, this code will take care of generating the coefficient value only if the custom field is defined, because this file is also a generic template for all modules of Dolibarr.&lt;br /&gt;
&lt;br /&gt;
At this point, everything is done, you should see the following:&lt;br /&gt;
[[File:Cf_wiki_coeff_column.png]]&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in margin module ===&lt;br /&gt;
&lt;br /&gt;
If you enabled the margin module, you should see that the coefficient is not applied there, but it&#039;s easy to fix that.&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/class/commonobject.class.php and find the function getMarginInfos(), then inside find the following lines:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach($this-&amp;gt;lines as $line) {&lt;br /&gt;
    if (empty($line-&amp;gt;pa_ht) &amp;amp;&amp;amp; isset($line-&amp;gt;fk_fournprice) &amp;amp;&amp;amp; !$force_price) {&lt;br /&gt;
        $product = new ProductFournisseur($this-&amp;gt;db);&lt;br /&gt;
        if ($product-&amp;gt;fetch_product_fournisseur_price($line-&amp;gt;fk_fournprice))&lt;br /&gt;
            $line-&amp;gt;pa_ht = $product-&amp;gt;fourn_unitprice * (1 - $product-&amp;gt;fourn_remise_percent / 100);&lt;br /&gt;
        if (isset($conf-&amp;gt;global-&amp;gt;MARGIN_TYPE) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;MARGIN_TYPE == &amp;quot;2&amp;quot; &amp;amp;&amp;amp; $product-&amp;gt;fourn_unitcharges &amp;gt; 0)&lt;br /&gt;
            $line-&amp;gt;pa_ht += $product-&amp;gt;fourn_unitcharges;&lt;br /&gt;
    }&lt;br /&gt;
    // si prix d&#039;achat non renseigné et devrait l&#039;être, alors prix achat = prix vente&lt;br /&gt;
    if ((!isset($line-&amp;gt;pa_ht) || $line-&amp;gt;pa_ht == 0) &amp;amp;&amp;amp; $line-&amp;gt;subprice &amp;gt; 0 &amp;amp;&amp;amp; (isset($conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull == 1)) {&lt;br /&gt;
        $line-&amp;gt;pa_ht = $line-&amp;gt;subprice * (1 - ($line-&amp;gt;remise_percent / 100));&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the lines are filled with margin infos. The price has just been fetched here, and we will now modify it with the coefficient before the price gets used for margin computation.&lt;br /&gt;
&lt;br /&gt;
Just below the code we found, paste the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $line-&amp;gt;table_element;&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
if (!empty($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
    $line-&amp;gt;pa_ht = $line-&amp;gt;pa_ht * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient;&lt;br /&gt;
    $line-&amp;gt;subprice = $line-&amp;gt;subprice * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s all, the price should be reflected in the margin.&lt;br /&gt;
[[File:Cf_wiki_coeff_margin.png]]&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in remaining debt to pay ===&lt;br /&gt;
&lt;br /&gt;
Open /htdocs/core/class/commonobject.class.php and look for the function update_price().&lt;br /&gt;
&lt;br /&gt;
You should see the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function update_price($exclspec=0,$roundingadjust=&#039;none&#039;,$nodatabaseupdate=0,$seller=&#039;&#039;)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just below the include_once line, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;;&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/conf/conf_customfields_func.lib.php&#039;;&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $this-&amp;gt;table_element_line);&lt;br /&gt;
// Fetching line ids from parent object id&lt;br /&gt;
$line_ids = $customfields-&amp;gt;fetchAny(&#039;rowid&#039;, MAIN_DB_PREFIX.$this-&amp;gt;table_element_line, $this-&amp;gt;fk_element.&#039; = &#039;.$this-&amp;gt;id);&lt;br /&gt;
$line_ids = array_values_recursive(&#039;rowid&#039;, $line_ids);&lt;br /&gt;
// Load custom fields&lt;br /&gt;
$cf_records = $customfields-&amp;gt;fetch($line_ids);&lt;br /&gt;
// Reassociate the records with the line id (instead of the customfields record id)&lt;br /&gt;
$cf_line_cfrowid_to_rowid = array_reassociate(&#039;rowid&#039;, &#039;fk_&#039;.$this-&amp;gt;table_element_line, $cf_records);&lt;br /&gt;
$tmp = $cf_records;&lt;br /&gt;
$cf_records = array();&lt;br /&gt;
foreach ($tmp as $k=&amp;gt;$v) { $cf_records[$cf_line_cfrowid_to_rowid[$k]] = $tmp[$k]; }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will load the coefficient custom field (and also any other custom field you have defined for lines).&lt;br /&gt;
&lt;br /&gt;
Now scroll below, and you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
while ($i &amp;lt; $num)&lt;br /&gt;
{&lt;br /&gt;
    $obj = $this-&amp;gt;db-&amp;gt;fetch_object($resql);&lt;br /&gt;
&lt;br /&gt;
    // Note: There is no check on detail line and no check on total, if $forcedroundingmode = &#039;none&#039;&lt;br /&gt;
&lt;br /&gt;
    if ($forcedroundingmode == &#039;0&#039;)	// Check if data on line are consistent. This may solve lines that were not consistent because set with $forcedroundingmode=&#039;auto&#039;&lt;br /&gt;
    {&lt;br /&gt;
        $localtax_array=array($obj-&amp;gt;localtax1_type,$obj-&amp;gt;localtax1_tx,$obj-&amp;gt;localtax2_type,$obj-&amp;gt;localtax2_tx);&lt;br /&gt;
        $tmpcal=calcul_price_total($obj-&amp;gt;qty, $obj-&amp;gt;up, $obj-&amp;gt;remise_percent, $obj-&amp;gt;vatrate, $obj-&amp;gt;localtax1_tx, $obj-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $obj-&amp;gt;info_bits, $obj-&amp;gt;product_type, $seller, $localtax_array);&lt;br /&gt;
        $diff=price2num($tmpcal[1] - $obj-&amp;gt;total_tva, &#039;MT&#039;, 1);&lt;br /&gt;
        if ($diff)&lt;br /&gt;
        {&lt;br /&gt;
            $sqlfix=&amp;quot;UPDATE &amp;quot;.MAIN_DB_PREFIX.$this-&amp;gt;table_element_line.&amp;quot; SET &amp;quot;.$fieldtva.&amp;quot; = &amp;quot;.$tmpcal[1].&amp;quot;, total_ttc = &amp;quot;.$tmpcal[2].&amp;quot; WHERE rowid = &amp;quot;.$obj-&amp;gt;rowid;&lt;br /&gt;
            dol_syslog(&#039;We found unconsistent data into detailed line (difference of &#039;.$diff.&#039;) for line rowid = &#039;.$obj-&amp;gt;rowid.&amp;quot; (total vat of line calculated=&amp;quot;.$tmpcal[1].&amp;quot;, database=&amp;quot;.$obj-&amp;gt;total_tva.&amp;quot;). We fix the total_vat and total_ttc of line by running sqlfix = &amp;quot;.$sqlfix);&lt;br /&gt;
            $resqlfix=$this-&amp;gt;db-&amp;gt;query($sqlfix);&lt;br /&gt;
            if (! $resqlfix) dol_print_error($this-&amp;gt;db,&#039;Failed to update line&#039;);&lt;br /&gt;
            $obj-&amp;gt;total_tva = $tmpcal[1];&lt;br /&gt;
            $obj-&amp;gt;total_ttc = $tmpcal[2];&lt;br /&gt;
            //&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just after, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
$cf_coeff = $cf_records[$obj-&amp;gt;rowid]-&amp;gt;coefficient;&lt;br /&gt;
if (empty($cf_coeff) and $cf_coeff !== 0) $cf_coeff = 1; // by default, an empty coefficient = 1&lt;br /&gt;
$obj-&amp;gt;total_ht = $obj-&amp;gt;total_ht * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_tva = $obj-&amp;gt;total_tva * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_localtax1 = $obj-&amp;gt;total_localtax1 * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_localtax2 = $obj-&amp;gt;total_localtax2 * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_ttc = $obj-&amp;gt;total_ttc * $cf_coeff;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should do the trick. Now go to one of your invoices (or whatever module you are targetting), and try to add/edit a product/service line to force refresh the total price. You should then see this:&lt;br /&gt;
[[File:Cf_wiki_coeff_remaining_debt.png]]&lt;br /&gt;
&lt;br /&gt;
= Custom tax per product =&lt;br /&gt;
&lt;br /&gt;
This part will show you how to create a static custom tax per product. Static means that the tax will be stored per product, and not per product&#039;s line, thus you just have to setup the tax once per product and it will always be applied in every invoice you add this product.&lt;br /&gt;
&lt;br /&gt;
To do that, you will need the CustomFields Pro module + ProductsEasyAccess.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
1- Create a product&#039;s custom fields called &amp;quot;custom_tax&amp;quot;. Now, using ProductsEasyAccess, you can access this field wherever you wants, either to show it on the Dolibarr&#039;s interface (see previous chapter), or in a PDF template, which we will describe below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2- Modify your invoice&#039;s PDF template. You first need to load all the products&#039; fields &#039;&#039;before&#039;&#039; the loop over the products&#039; lines. To do this, search for a line like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// This should be placed above the &amp;quot;loop lines&amp;quot; below, add this:&lt;br /&gt;
&lt;br /&gt;
dol_include_once(&#039;/productseasyaccess/lib/productseasyaccess.lib.php&#039;);&lt;br /&gt;
fill_object_with_products_fields($object);&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines, search for this command&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  // Add this just below the for loop, this will ease your php manipulation after.&lt;br /&gt;
  // This will allow you to more easily access the products&#039; fields (this will store the id of each product inside $lineproductid).&lt;br /&gt;
  $line_product_id = $object-&amp;gt;lines[$i]-&amp;gt;fk_product;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4- You can now use your products&#039; fields in your PDF. To access the custom_tax field, just use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And to print it in your PDF:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell(0,3, $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax, 0, &#039;L&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5- Then, if you want to count the custom tax total and add it to your vat total, you can create a counter inside the for loop we cited in the first step:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_custom_tax = 0; // Initialize the custom tax total counter&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  $object-&amp;gt;total_custom_tax += $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then find inside the function **_tableau_tot**, find the following line:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And prepend the total modification like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc + $object-&amp;gt;total_custom_tax; // prepend this to add the custom tax to the total&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Done. This should show your custom tax per product and your total including all custom taxes.&lt;/div&gt;</summary>
		<author><name>Lrq3000</name></author>
	</entry>
	<entry>
		<id>https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33070</id>
		<title>Module CustomFields Cases</title>
		<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33070"/>
		<updated>2015-01-15T15:48:32Z</updated>

		<summary type="html">&lt;p&gt;Lrq3000: /* Implementing the change of price in margin module */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:CustomFields]]&lt;br /&gt;
{{TemplateDocDevEn}}&lt;br /&gt;
{{TemplateModEN}}&lt;br /&gt;
{{BasculeDevUserEn|&lt;br /&gt;
name=CustomFields|&lt;br /&gt;
num=8500|&lt;br /&gt;
devdoc=[[Module_CustomFields_Dev]]|&lt;br /&gt;
userdoc=[[Module_CustomFields]]|}}&lt;br /&gt;
This page lists a compilation of concrete, practical examples uses of [[Module_CustomFields|CustomFields]] and of its various features.&lt;br /&gt;
&lt;br /&gt;
If you had an experience with CustomFields, you can also add here your case as an example for others to get inspired.&lt;br /&gt;
&lt;br /&gt;
= How to make a generic PDF template listing all custom fields =&lt;br /&gt;
&lt;br /&gt;
To make a generic PDF template listing all custom fields, you just need to take an existing PDF template, then you need to follow these 3 steps:&lt;br /&gt;
&lt;br /&gt;
1/ Edit the class name (required, else it will conflict with the original template).&lt;br /&gt;
&lt;br /&gt;
Eg: if we take the crabe template for invoices, we change:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;crabe&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
into:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_customfields extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;customfields&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2/ Copy this generic function at the end of the PDF:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propale/order/etc... (CustomFields simpler functions will automatically adapt)&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs); // set default PDF font size&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
    // Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
    $customfields = customfields_fill_object($object, null, $outputlangs, null, true);&lt;br /&gt;
&lt;br /&gt;
    // Setting the starting position of the text cursor&lt;br /&gt;
    $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
    $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
    // Printing the customfields&lt;br /&gt;
    foreach ($object-&amp;gt;customfields as $label=&amp;gt;$value) { // $value is already formatted!&lt;br /&gt;
        // Get translated label&lt;br /&gt;
        $translatedlabel = $customfields-&amp;gt;findLabelPDF($label, $outputlangs); // translated label of the customfield (not translated by default in customfields_fill_object() because a field should always be accessible by a base name, whatever the translation is)&lt;br /&gt;
&lt;br /&gt;
        // PDF formatting, placement and printing&lt;br /&gt;
        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
        $pdf-&amp;gt;MultiCell(0,3, $translatedlabel.&#039; ($object-&amp;gt;customfields-&amp;gt;&#039;.$label.&#039;): &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternative function (with a bit less functionalities, but more controllable):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page (used to debug if CustomFields setup is correct)&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propal/product/whatever...&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs);&lt;br /&gt;
&lt;br /&gt;
            if (empty($object-&amp;gt;table_element) or empty($object-&amp;gt;id)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;Current \$object is not compatible with CustomFields, could not find table_element or id.&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
    $customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element);&lt;br /&gt;
&lt;br /&gt;
            // Fetching custom fields records&lt;br /&gt;
            $fields = $customfields-&amp;gt;fetch($object-&amp;gt;id);&lt;br /&gt;
&lt;br /&gt;
            if (!isset($fields)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;No custom field could be found for this object. Please check your configuration (did you create at least one customfield and set a value in the current datasheet?)&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            } else {&lt;br /&gt;
                // Setting the starting position of the text cursor&lt;br /&gt;
                $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
                $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
                // Printing the customfields&lt;br /&gt;
                foreach ($fields as $key=&amp;gt;$field) {&lt;br /&gt;
                        $translatedname = $customfields-&amp;gt;findLabelPDF($key, $outputlangs); // label of the customfield&lt;br /&gt;
                        $value = $customfields-&amp;gt;simpleprintFieldPDF($key, $field, $outputlangs); // value (cleaned and properly formatted) of the customfield&lt;br /&gt;
&lt;br /&gt;
                        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
                        $pdf-&amp;gt;MultiCell(0,3, $translatedname.&#039;: &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
                        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3/ Call the _pagecustomfields function at the right place (generally, after _pagefoot and just before $pdf-&amp;gt;Close()):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Pied de page&lt;br /&gt;
$this-&amp;gt;_pagefoot($pdf,$object,$outputlangs);&lt;br /&gt;
$pdf-&amp;gt;AliasNbPages();&lt;br /&gt;
&lt;br /&gt;
// CustomFields&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) { // if the customfields module is activated...&lt;br /&gt;
        // Add a page with the customfields&lt;br /&gt;
        $pdf-&amp;gt;AddPage();&lt;br /&gt;
        $this-&amp;gt;_pagecustomfields($pdf,$object,$outputlangs);&lt;br /&gt;
        $pagenb++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
$pdf-&amp;gt;Close(); // you should first search for this, you should only find one occurrence&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then go to the module&#039;s admin panel (eg: invoice admin panel) and enable the template you&#039;ve just created.&lt;br /&gt;
&lt;br /&gt;
You can now use it to test if your customfields work.&lt;br /&gt;
&lt;br /&gt;
= Conditions on fields selection =&lt;br /&gt;
&lt;br /&gt;
Sometimes you may want to show a DropdownBox or Constraint (or another multi-choice type), but you may want to show or hide choices based on conditions (eg: only if it&#039;s a prospect, only if this zone was selected, only if this category was selected, etc..).&lt;br /&gt;
&lt;br /&gt;
To know which is the best solution you should choose to implement your condition, you should first know in which category your condition falls in.&lt;br /&gt;
&lt;br /&gt;
== Conditions categories ==&lt;br /&gt;
&lt;br /&gt;
Conditions can be categorized into 4 types:&lt;br /&gt;
&lt;br /&gt;
* static condition: choices are restrained on a condition that never changes (eg: only show third-parties that are suppliers, or lowercase all data on sql insertion, or check that a field is above a certain number, etc.).&lt;br /&gt;
* semi-dynamic condition: choices that are constrained on a static table but based on the value of another field/customfield. This is also commonly called &amp;quot;Cascaded dropdown lists&amp;quot; or &amp;quot;Dynamically linked dropdown lists&amp;quot;.&lt;br /&gt;
* dynamic: everything is dynamic: choices are based on a subfield of a field of the current module, so that there&#039;s no material table and it&#039;s impossible to create a view (because you would need to create one view per id) (eg: show only the contacts attached to the third-party being accessed)&lt;br /&gt;
* timed condition: a condition that is based on time or recurrence (eg: at 5:00 AM delete old records, etc..)&lt;br /&gt;
&lt;br /&gt;
== Conditions solutions ==&lt;br /&gt;
&lt;br /&gt;
Now that you know what kind of condition you want to set, you can choose one of the following solutions depending on your preferences and knowledge.&lt;br /&gt;
&lt;br /&gt;
In any case, you can always use the &#039;&#039;&#039;CustomFields&#039;s overload functions&#039;&#039;&#039; to fit your needs for nearly every type of condition.&lt;br /&gt;
&lt;br /&gt;
=== Static condition ===&lt;br /&gt;
&lt;br /&gt;
* View: create a view on the table based on the condition, and then create a custom field constrained on this view (which is just like any table), eg: only show third-parties that are suppliers:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
CREATE VIEW llx_societe_supplier_view AS&lt;br /&gt;
SELECT *&lt;br /&gt;
FROM llx_societe&lt;br /&gt;
WHERE fournisseur=1;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
WARNING: foreign keys (and thus constraints) can&#039;t be used on views with MySQL, and materialized views neither exist, so you can&#039;t use this method with MySQL.&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
* SQL Transformations&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
=== Semi-dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade option is ideal in most cases, and if not enough, you can use the Cascade Custom option and Overloading Functions.&lt;br /&gt;
* View&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
&lt;br /&gt;
=== Dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade Custom and Overload functions&lt;br /&gt;
* Making your own module, which calls the CustomFields class&lt;br /&gt;
&lt;br /&gt;
=== Timed condition ===&lt;br /&gt;
&lt;br /&gt;
* SQL scheduled events&lt;br /&gt;
* Cron job&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
= Overloading functions =&lt;br /&gt;
&lt;br /&gt;
Here are a few concrete use cases of overloading functions to achieve various goals.&lt;br /&gt;
&lt;br /&gt;
== Dynamic list selection based on another one ==&lt;br /&gt;
&lt;br /&gt;
The goal is to show two custom fields on the Third-Party module: one which gives the zone (secteur) of the third-party (Isere, Alpes du sud, Haute-Savoie...) and the other which gives relative to the zone the list of all the ski resorts (station_a) inside the selected zone.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will show how to manually manage this kind of requirements using [[Module_CustomFields#Overloading_functions:_Adding_your_own_management_code_for_custom_fields|overloading functions]], but in latest CustomFields releases, the same solution can be achieved automatically using the [[Module_CustomFields#Cascade|Cascade option]].&lt;br /&gt;
&lt;br /&gt;
We have one table &#039;&#039;&#039;llx_k_station&#039;&#039;&#039; (rowid, station (char50), secteur(char50)) which contains the following: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1,les 2 alpes,isère&lt;br /&gt;
2,chamrousse,isère&lt;br /&gt;
3,alpe d&#039;huez,isère&lt;br /&gt;
4,vars,alpes du sud&lt;br /&gt;
5,risoul,alpes du sud&lt;br /&gt;
etc...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In CustomFields&#039;s admin panel, we create two custom fields:&lt;br /&gt;
&lt;br /&gt;
1- secteur: returns the list of all zones - type DropdownBox: enum(&#039;- Aucun&#039;,&#039;Alpes du Sud&#039;,&#039;export&#039;,&#039;Haute-Savoie&#039;,&#039;Isère&#039;, etc...)&lt;br /&gt;
&lt;br /&gt;
2- station_a: returns the list of all ski resorts - type constraint on &#039;&#039;&#039;llx_k_station&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Here is the code to put in customfields_fields_extend.lib.php that will allow to show only the ski resorts corresponding to the selected zone:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_editview_societe_station_a (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$parameters, &amp;amp;$action, &amp;amp;$user, &amp;amp;$idvar, &amp;amp;$rightok, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
  global $db; // allow to access database functions&lt;br /&gt;
  $sql=&amp;quot;SELECT llx_k_station.station, llx_k_station.rowid FROM llx_societe_customfields INNER JOIN llx_k_station ON llx_societe_customfields.Secteur = llx_k_station.secteur WHERE llx_societe_customfields.fk_societe=&amp;quot;.$object-&amp;gt;id;&lt;br /&gt;
$result=$db-&amp;gt;query($sql);&lt;br /&gt;
 &lt;br /&gt;
  if ($result) {&lt;br /&gt;
    $num = $db-&amp;gt;num_rows($result);&lt;br /&gt;
    $i = 0;&lt;br /&gt;
    $myarray = array();&lt;br /&gt;
    while ($i &amp;lt; $num)&lt;br /&gt;
    {&lt;br /&gt;
      $obj = $db-&amp;gt;fetch_object($result);&lt;br /&gt;
      $myarray []=array(&#039;id&#039;=&amp;gt;$obj-&amp;gt;rowid, &#039;value&#039;=&amp;gt;$obj-&amp;gt;station);&lt;br /&gt;
      $i++;&lt;br /&gt;
    }&lt;br /&gt;
    $value = $myarray; &lt;br /&gt;
    $db-&amp;gt;free($result);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to manub for giving this case.&lt;br /&gt;
&lt;br /&gt;
== Dynamic discount computation per products lines on propales ==&lt;br /&gt;
&lt;br /&gt;
The goal here is to compute the discount for a product line in propales based on a computation on three custom fields.&lt;br /&gt;
&lt;br /&gt;
First, create three custom fields for &amp;quot;Line commercial proposals&amp;quot;: vl, cpl and lc.&lt;br /&gt;
&lt;br /&gt;
Then add the following overloading function at the end of your customfields_fields_extend.lib.php file (read the comments for more informations):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_propaldet_vl (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
&lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $vl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;vl&#039;};&lt;br /&gt;
    $cpl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;cpl&#039;};&lt;br /&gt;
    $lc = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;lc&#039;};&lt;br /&gt;
&lt;br /&gt;
    // Compute the discount&lt;br /&gt;
    $remise_calculee = (100 - $vl) * (100 - $cpl) * (100 - $lc);&lt;br /&gt;
&lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $remise_calculee = price2num($remise_calculee);&lt;br /&gt;
&lt;br /&gt;
    // Assign the new discount into the PropalLine object&lt;br /&gt;
    $object-&amp;gt;remise_percent = $remise_calculee;&lt;br /&gt;
&lt;br /&gt;
    // Update the price (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
&lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
&lt;br /&gt;
    //$q = &amp;quot;UPDATE  &amp;quot;.MAIN_DB_PREFIX.&amp;quot;propaldet SET remise_percent=&amp;quot;.$remise_calculee.&amp;quot; WHERE  rowid=&amp;quot;.$object-&amp;gt;rowid;&lt;br /&gt;
    //$db-&amp;gt;query($q);&lt;br /&gt;
    &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;vl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;cpl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;lc&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Linking Dolibarr objects from two different modules =&lt;br /&gt;
&lt;br /&gt;
Dolibarr sometimes offers the possibility to link two different modules. Eg: when you convert an Intervention card into an Invoice.&lt;br /&gt;
&lt;br /&gt;
In this case, you might want in your templates to fetch the custom fields of the linked object. Eg: you generate a PDF from the Invoice, but you want to get the custom fields from the Intervention that was converted into the Invoice.&lt;br /&gt;
&lt;br /&gt;
The key here is to use the &#039;&#039;&#039;llx_element_element&#039;&#039;&#039; table or the &#039;&#039;&#039;llx_element_*&#039;&#039;&#039; tables (eg: llx_element_contact): this is a standard Dolibarr table that stores all links between objects of two different modules.&lt;br /&gt;
&lt;br /&gt;
Here is the structure:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
rowid | fk_source | sourcetype | fk_target | targettype&lt;br /&gt;
-------------------------------------------------------&lt;br /&gt;
1 | 2 | fichinter | 5 | facture&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can then use a php code like the following to populate your $object with customfields:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
// This will also create and returns us an instance of the CustomFields class, which we will use to manually query the llx_element_element table&lt;br /&gt;
$customfields = customfields_fill_object($object, null, $outputlangs, &#039;facture&#039;, true); // beautified values&lt;br /&gt;
&lt;br /&gt;
// Querying the llx_element_element table manually to get the link between the invoice and the linked intervention card (we want the id of the intervention card)&lt;br /&gt;
// fetchAny($columns, $table, $where=&#039;&#039;, $orderby=&#039;&#039;, $limitby=&#039;&#039;)&lt;br /&gt;
$eltrow = $customfields-&amp;gt;fetchAny(&#039;*&#039;, MAIN_DB_PREFIX.&#039;element_element&#039;, &#039;targettype=&amp;quot;&#039;.$object-&amp;gt;element.&#039;&amp;quot; and fk_target=&#039;.$object-&amp;gt;id.&#039; and sourcetype=&amp;quot;fichinter&amp;quot;&#039;);&lt;br /&gt;
&lt;br /&gt;
// Save the intervention&#039;s id&lt;br /&gt;
$sourceid = $eltrow[0][&#039;fk_source&#039;];&lt;br /&gt;
&lt;br /&gt;
// Crafting a dummy intervention object&lt;br /&gt;
$fromobject = new stdClass(); // Init an empty object&lt;br /&gt;
$fromobject-&amp;gt;id = $sourceid; // We need the id and..&lt;br /&gt;
$fromobject-&amp;gt;table_element = &#039;fichinter&#039;; // the table_element (name of the table)&lt;br /&gt;
&lt;br /&gt;
// We can then fill $object with the intervention custom fields (we store them in a sub property &#039;fichinter&#039; to avoid conflicts with invoice&#039;s custom fields)&lt;br /&gt;
customfields_fill_object($object, $fromobject, $outputlangs, &#039;fichinter&#039;, true);&lt;br /&gt;
&lt;br /&gt;
// Now you can access invoices custom fields using:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;facture-&amp;gt;cf_myfield);&lt;br /&gt;
// And access intervention custom fields with:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;fichinter-&amp;gt;cf_myfield);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to netassopro for the tip.&lt;br /&gt;
&lt;br /&gt;
= Modify the total global price with a coefficient (without tampering the discount) =&lt;br /&gt;
&lt;br /&gt;
This is more troublesome than modifying the total price per product/service line, because the total global price of an object (let&#039;s say an invoice, but this also works for any other object) is always recomputed by summing the prices and quantity of each product/service line. This means that although there&#039;s an entry &amp;quot;total_ttc&amp;quot; in the database to store the invoice&#039;s total price, this price is in fact always recomputed, it&#039;s never used as-is. Thus, it&#039;s useless to change only the database record: we need to inject some code to change how the total price is computed.&lt;br /&gt;
&lt;br /&gt;
This could be done using an overloading function or a trigger, but unluckily, Dolibarr doesn&#039;t call a trigger nor a hook when recomputing the price. We thus need to edit core files to do what we want.&lt;br /&gt;
&lt;br /&gt;
However, the bright side is that we can cleverly edit just two functions from one core file: update_price() and getMarginInfos() from /htdocs/core/class/commonobject.class.php , and everything should run smoothly. Beware that if you update Dolibarr, you will need to redo these two modifications in the code.&lt;br /&gt;
&lt;br /&gt;
First, you need to create a new custom field called &amp;quot;global_coefficient&amp;quot; in Invoices (not Invoices Lines!). Then you can continue to the subchapters below.&lt;br /&gt;
&lt;br /&gt;
Also note that this global coefficient is also compatible with per line coefficients (as described in the next chapter), thus you can use a global coefficient and per line coefficients at the same time. You can also use multiple global coefficient if you wish.&lt;br /&gt;
&lt;br /&gt;
Another note: if you have a grid of discounts depending on quantity or price or whatever parameter, you can program a precomputation code using a Custom Ajax function (this way, your discounts will be automatically computed, but then you can still manually enter them for particular cases since the custom field is editable).&lt;br /&gt;
&lt;br /&gt;
== Implement the change of global price in most of Dolibarr (including remaining debt to pay and PDF and ODT) ==&lt;br /&gt;
&lt;br /&gt;
The change we will implement here will reflect the new total price with the global coefficient in most of Dolibarr&#039;s code, since we will here change the main function that is usually called to update and get the total price.&lt;br /&gt;
&lt;br /&gt;
Open /htdocs/core/class/commonobject.class.php and look for the function update_price().&lt;br /&gt;
&lt;br /&gt;
You should see the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function update_price($exclspec=0,$roundingadjust=&#039;none&#039;,$nodatabaseupdate=0,$seller=&#039;&#039;)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just below the include_once line, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;;&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/conf/conf_customfields_func.lib.php&#039;;&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $this-&amp;gt;table_element);&lt;br /&gt;
// Get object&#039;s id (either stored in property &amp;quot;id&amp;quot; or &amp;quot;rowid&amp;quot;)&lt;br /&gt;
$this_id = (!empty($this-&amp;gt;id)) ? $this-&amp;gt;id : $this-&amp;gt;rowid;&lt;br /&gt;
// Load custom fields records&lt;br /&gt;
$cf_parent_record = $customfields-&amp;gt;fetch($this_id);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will load the global_coefficient custom field (and also any other custom field you have defined for invoices).&lt;br /&gt;
&lt;br /&gt;
Now scroll below, and you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
while ($i &amp;lt; $num)&lt;br /&gt;
{&lt;br /&gt;
    $obj = $this-&amp;gt;db-&amp;gt;fetch_object($resql);&lt;br /&gt;
&lt;br /&gt;
    ... (lots of code)&lt;br /&gt;
&lt;br /&gt;
    i++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Add revenue stamp to total&lt;br /&gt;
$this-&amp;gt;total_ttc       += isset($this-&amp;gt;revenuestamp)?$this-&amp;gt;revenuestamp:0;&lt;br /&gt;
&lt;br /&gt;
$this-&amp;gt;db-&amp;gt;free($resql);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is the end of the loop that is summing each product/service line price to compute the total price. Just after this loop, you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Now update global field total_ht, total_ttc and tva&lt;br /&gt;
$fieldht=&#039;total_ht&#039;;&lt;br /&gt;
$fieldtva=&#039;tva&#039;;&lt;br /&gt;
$fieldlocaltax1=&#039;localtax1&#039;;&lt;br /&gt;
$fieldlocaltax2=&#039;localtax2&#039;;&lt;br /&gt;
$fieldttc=&#039;total_ttc&#039;;&lt;br /&gt;
// Specific code for backward compatibility with old field names&lt;br /&gt;
if ($this-&amp;gt;element == &#039;facture&#039; || $this-&amp;gt;element == &#039;facturerec&#039;)             $fieldht=&#039;total&#039;;&lt;br /&gt;
if ($this-&amp;gt;element == &#039;facture_fourn&#039; || $this-&amp;gt;element == &#039;invoice_supplier&#039;) $fieldtva=&#039;total_tva&#039;;&lt;br /&gt;
if ($this-&amp;gt;element == &#039;propal&#039;)                                                $fieldttc=&#039;total&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Between those two blocks of code (so just above // Now update global field total_ht, total_ttc and tva), you can place the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
if (!empty($cf_parent_record-&amp;gt;global_coefficient)) { // avoid applying the coefficient if empty (this will return a 0 price)&lt;br /&gt;
    $this-&amp;gt;total_ht = $this-&amp;gt;total_ht * $cf_parent_record-&amp;gt;global_coefficient;&lt;br /&gt;
    $this-&amp;gt;total_tva = $this-&amp;gt;total_tva * $cf_parent_record-&amp;gt;global_coefficient;&lt;br /&gt;
    $this-&amp;gt;total_ttc = $this-&amp;gt;total_ttc * $cf_parent_record-&amp;gt;global_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Done, now try to add/edit a product/service line, and the total price should be affected by the global coefficient.&lt;br /&gt;
&lt;br /&gt;
== Implementing the change of price in margin module ==&lt;br /&gt;
&lt;br /&gt;
If you enabled the margin module, you should see that the coefficient is not applied there, but it&#039;s easy to fix that.&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/class/commonobject.class.php and find the function getMarginInfos(), then inside find the following lines:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach($this-&amp;gt;lines as $line) {&lt;br /&gt;
    if (empty($line-&amp;gt;pa_ht) &amp;amp;&amp;amp; isset($line-&amp;gt;fk_fournprice) &amp;amp;&amp;amp; !$force_price) {&lt;br /&gt;
        $product = new ProductFournisseur($this-&amp;gt;db);&lt;br /&gt;
        if ($product-&amp;gt;fetch_product_fournisseur_price($line-&amp;gt;fk_fournprice))&lt;br /&gt;
            $line-&amp;gt;pa_ht = $product-&amp;gt;fourn_unitprice * (1 - $product-&amp;gt;fourn_remise_percent / 100);&lt;br /&gt;
        if (isset($conf-&amp;gt;global-&amp;gt;MARGIN_TYPE) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;MARGIN_TYPE == &amp;quot;2&amp;quot; &amp;amp;&amp;amp; $product-&amp;gt;fourn_unitcharges &amp;gt; 0)&lt;br /&gt;
            $line-&amp;gt;pa_ht += $product-&amp;gt;fourn_unitcharges;&lt;br /&gt;
    }&lt;br /&gt;
    // si prix d&#039;achat non renseigné et devrait l&#039;être, alors prix achat = prix vente&lt;br /&gt;
    if ((!isset($line-&amp;gt;pa_ht) || $line-&amp;gt;pa_ht == 0) &amp;amp;&amp;amp; $line-&amp;gt;subprice &amp;gt; 0 &amp;amp;&amp;amp; (isset($conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull == 1)) {&lt;br /&gt;
        $line-&amp;gt;pa_ht = $line-&amp;gt;subprice * (1 - ($line-&amp;gt;remise_percent / 100));&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the lines are filled with margin infos. The price has just been fetched here, and we will now modify it with the coefficient before the price gets used for margin computation.&lt;br /&gt;
&lt;br /&gt;
Just below the code we found (and just above the comment: &amp;quot;// calcul des marges&amp;quot;), paste the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
// Get object&#039;s id (either stored in property &amp;quot;id&amp;quot; or &amp;quot;rowid&amp;quot;)&lt;br /&gt;
$this_id = (!empty($this-&amp;gt;id)) ? $this-&amp;gt;id : $this-&amp;gt;rowid;&lt;br /&gt;
// Load custom fields records&lt;br /&gt;
$customfields = customfields_fill_object($this);&lt;br /&gt;
// Apply the coefficient&lt;br /&gt;
if (!empty($this-&amp;gt;customfields-&amp;gt;cf_global_coefficient)) {&lt;br /&gt;
    $line-&amp;gt;pa_ht = $line-&amp;gt;pa_ht * $this-&amp;gt;customfields-&amp;gt;cf_global_coefficient;&lt;br /&gt;
    $line-&amp;gt;subprice = $line-&amp;gt;subprice * $this-&amp;gt;customfields-&amp;gt;cf_global_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that here we cheated a bit: we do not compute the coefficient on the global total price, but rather per product/service line price. This is done on purpose, because mathematically this should produce no difference, and it also allows Dolibarr to continue peacefully with its margin calculations (else we would have to adapt all those computations, which would imply a lot more of code changes!).&lt;br /&gt;
&lt;br /&gt;
That&#039;s all, the price should be reflected in the margin. Just refresh the page to reflect the change.&lt;br /&gt;
&lt;br /&gt;
= Modify the total price PER product/service line with a coefficient (without tampering the discount) =&lt;br /&gt;
&lt;br /&gt;
Here we will go through the creation and management of a custom field called &#039;&#039;&#039;coefficient&#039;&#039;&#039; that will change the total price of each product by modifying it by the coefficient.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will not create a coefficient per product but rather per product&#039;s line. Thus if you want to create a coefficient that never change between invoices but only depending on the product and stays static, please refer to the next chapter below about creating a custom tax per product.&lt;br /&gt;
&lt;br /&gt;
The preliminary and necessary step is to first create a custom field that will be used to modify the total price. We will create one for Client Invoice Lines.&lt;br /&gt;
&lt;br /&gt;
Go to the CustomFields administration panel, then into the Client Invoice Lines tab, then click on the button New Field, name the field &#039;&#039;&#039;coefficient&#039;&#039;&#039;, and set the type to &#039;&#039;&#039;Double&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
When you go to client invoices in Dolibarr, you should then see something similar to this:&lt;br /&gt;
[[File:Cfe1.jpg]]&lt;br /&gt;
You can see that our coefficient field was successfully created.&lt;br /&gt;
&lt;br /&gt;
Then here are two alternative ways of implementing the change of the total price of each product relativery to this coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change total price in database ==&lt;br /&gt;
&lt;br /&gt;
Here we will use a &amp;quot;save&amp;quot; overloading function to change the total price directly in the database, thus we won&#039;t have to modify anything else (eg: nor PDF templating nor ODT) since the total price will be already updated in the database.&lt;br /&gt;
&lt;br /&gt;
Warning: this method will work only with CustomFields =&amp;gt; 3.2.23 since the &amp;quot;save&amp;quot; overloading function did not work for products lines before.&lt;br /&gt;
&lt;br /&gt;
Just add the following overloading function in your customfields_fields_extend.lib.php file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_facturedet_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
 &lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $coefficient = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;coefficient&#039;}; // you could also use $coefficient = $value;&lt;br /&gt;
 &lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $coefficient = price2num($coefficient);&lt;br /&gt;
 &lt;br /&gt;
    // Get the original subprices (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
 &lt;br /&gt;
    // Update the subprices with the coefficient&lt;br /&gt;
    $object-&amp;gt;total_ht = $object-&amp;gt;total_ht * $coefficient; // compute no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva = $object-&amp;gt;total_tva * $coefficient; // compute tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc * $coefficient; // compute tax included price&lt;br /&gt;
 &lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(&#039;&#039;,true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
 &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;coefficient&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should show the correct total price on the Dolibarr&#039;s interface without modifying the unit subprice (P.U. Unit):&lt;br /&gt;
&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
You have nothing to edit in your PDF or ODT template, since the new subprice with applied coefficient will be automatically printed instead of the old one without coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change printed total price per product without tampering the subprice in the database ==&lt;br /&gt;
&lt;br /&gt;
Here is a way to change the printed total price (the one shown on screen and in PDF/ODT) but without changing the total price in the database.&lt;br /&gt;
&lt;br /&gt;
The following sub-chapters are all independent, thus you can only follow the ones that you require, eg: if you only need to change the price in your PDF documents, just create a custom field then jump to the sub-chapter to &#039;&#039;&#039;Implement the change of price in PDF&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in ODT templates ===&lt;br /&gt;
&lt;br /&gt;
There&#039;s no eval implementation currently inside ODT templates, but there is a kind of hook implementation, called [[Create_an_ODT_document_template#Other_personalized_tags|substitution functions]].&lt;br /&gt;
&lt;br /&gt;
You can either create your own module, then create your own substitution function, but for the sake of simplicity in this tutorial, we will directly use the CustomFields substitution function. However, please remember that you will have to redo your changes everytime you will update the CustomFields module, so if you don&#039;t want to do that it would be better if you create your own module and substitution function.&lt;br /&gt;
&lt;br /&gt;
Open the file &#039;&#039;&#039;htdocs/customfields/core/substitutions/functions_customfields.lib.php&#039;&#039;&#039; and go to the function &#039;&#039;&#039;customfields_completesubstitutionarray_lines&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
At the very end of this function, but inside the customfields check:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) {&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
But just before the ending bracket, you should add the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($object-&amp;gt;table_element_line === &#039;facturedet&#039;) { // First check that we are in the right module, we don&#039;t want to use coefficient for other modules if we didn&#039;t define the custom field there&lt;br /&gt;
    $substitutionarray[&#039;line_price_full&#039;] = $substitutionarray[&#039;line_price_ttc&#039;] * $substitutionarray[&#039;cf_coefficient&#039;]; // multiply the total price with our coefficient&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code will add a new tag &#039;&#039;&#039;{line_price_full}&#039;&#039;&#039; that you can then use inside your ODT templates.&lt;br /&gt;
&lt;br /&gt;
Just place this tag in a table in your ODT document, between the table tags ( [!-- BEGIN row.lines --] ... [!-- END row.lines --] ), and this will print your full price.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in PDF templates ===&lt;br /&gt;
&lt;br /&gt;
First, we will create our own PDF template by copying an existing template, and then we will edit it to change the price of items.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Creating your own PDF template by copying an existing template&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Go into the following folder: &#039;&#039;&#039;htdocs/core/modules/facture/doc/&#039;&#039;&#039;, then copy the file &#039;&#039;&#039;pdf_crabe.modules.php&#039;&#039;&#039; and name the new one &#039;&#039;&#039;pdf_crabecoeff.modules.php&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Then open it in your favourite editor, and change the following lines:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures // change pdf_crabe into pdf_crabecoeff&lt;br /&gt;
{&lt;br /&gt;
...&lt;br /&gt;
    function __construct($db)&lt;br /&gt;
    {&lt;br /&gt;
        ...&lt;br /&gt;
	$this-&amp;gt;name = &amp;quot;crabe&amp;quot;; // change &amp;quot;crabe&amp;quot; into &amp;quot;crabecoeff&amp;quot;&lt;br /&gt;
        ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you have your own PDF template!&lt;br /&gt;
&lt;br /&gt;
Now you should &#039;&#039;&#039;enable your PDF template&#039;&#039;&#039; before doing anything else, by going into the Modules Administration panel, and into the Invoice admin page, then click on the ON button at the right of the &#039;&#039;&#039;crabecoeff&#039;&#039;&#039; entry to enable the template.&lt;br /&gt;
&lt;br /&gt;
Now, let&#039;s proceed onto modifying the prices.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Changing the prices automatically in your PDF&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
You can now do pretty much anything you want in your PDF template, but I will give you a mean to automatically change the prices of every products with our coefficient, as well as the total price, without editing too much the template.&lt;br /&gt;
&lt;br /&gt;
First, try to find the following code of line (should be inside the write_file() method):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf=pdf_getInstance($this-&amp;gt;format);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, just copy/paste the following code &#039;&#039;&#039;below&#039;&#039;&#039; pdf_getInstance():&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$this-&amp;gt;customfields = customfields_fill_object($object, null, $outputlangs, null, true); // beautified values&lt;br /&gt;
$this-&amp;gt;customfields_raw = customfields_fill_object($object, null, $outputlangs, &#039;raw&#039;, null); // raw values&lt;br /&gt;
$this-&amp;gt;customfields_lines = customfields_fill_object_lines($object, null, $outputlangs, null, true); // product lines&#039; values&lt;br /&gt;
&lt;br /&gt;
// Resetting every $object price because we will compute them anew&lt;br /&gt;
$object-&amp;gt;total_ht = 0;&lt;br /&gt;
$object-&amp;gt;total_tva = 0;&lt;br /&gt;
$object-&amp;gt;total_ttc = 0;&lt;br /&gt;
// For each line of product/service, we will recompute all the prices&lt;br /&gt;
foreach($object-&amp;gt;lines as $line) {&lt;br /&gt;
    // Get the coefficient custom field&#039;s value for the current line&lt;br /&gt;
    $coeff = $object-&amp;gt;customfields-&amp;gt;lines-&amp;gt;{$line-&amp;gt;rowid}-&amp;gt;cf_coefficient;&lt;br /&gt;
    // Recompute the price of this product/service line&lt;br /&gt;
    $line-&amp;gt;total_ht = $line-&amp;gt;total_ht*$coeff; // compute no tax price&lt;br /&gt;
    $line-&amp;gt;total_tva = $line-&amp;gt;total_tva*$coeff; // compute tax amount&lt;br /&gt;
    $line-&amp;gt;total_ttc = $line-&amp;gt;total_ttc*$coeff; // compute tax included price&lt;br /&gt;
    // Do the sum of all new products/services prices and tva (will be incremented with every product/service line)&lt;br /&gt;
    $object-&amp;gt;total_ht += $line-&amp;gt;total_ht; // global no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva += $line-&amp;gt;total_tva; // global vat/tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc += $line-&amp;gt;total_ttc; // global  tax included price&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you should try to generate your PDF document, and you should get something like this:&lt;br /&gt;
[[File:Cfe3.jpg]]&lt;br /&gt;
&lt;br /&gt;
Note: printing the coefficient as a column in your PDF document is also possible but will not be covered here because PDF templates will vary for every persons depending on your needs.&lt;br /&gt;
For guidelines about how to do this, you can read the documentation on [[Create_a_PDF_document_template|how to create a PDF template]] and [[Module_CustomFields#Implementing_in_PDF_templates|how to use custom fields in PDF templates]].&lt;br /&gt;
&lt;br /&gt;
Hint: you should be looking inside the following conditional block, inside the write_file() method:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
{&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of total price in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
Unluckily there is not (yet) any hooking context to modify how lines of products/services are printed, thus the only thing you can do is directly edit the &#039;&#039;&#039;template file&#039;&#039;&#039; and manually put inside your code to load your custom field and print it the way you want using HTML formatting, for our example:&lt;br /&gt;
&lt;br /&gt;
* For Dolibarr 3.3 and above, edit the file &#039;&#039;&#039;htdocs/core/tpl/objectline_view.tpl.php&#039;&#039;&#039;&lt;br /&gt;
* For Dolibarr 3.1 and 3.2, edit the file &#039;&#039;&#039;htdocs/core/tpl/freeproductline_view.tpl.php&#039;&#039;&#039; AND &#039;&#039;&#039;predefinedproductline_view.tpl.php&#039;&#039;&#039; (apply the same changes to both files)&lt;br /&gt;
&lt;br /&gt;
In the template file you opened, find the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And replace it by:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $object-&amp;gt;table_element_line; // you can use $object-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
&lt;br /&gt;
// Modifying the HT price and printing it on the Dolibarr&#039;s datasheet interface&lt;br /&gt;
// Applying a multiplying/dividing coefficient to an HT price will give a mathematically correct TTC price since the taxes are also a multiplier&lt;br /&gt;
// However, if you want to apply other mathematical operations (like addition or substaction), you should use the total price with $line-&amp;gt;total_ttc&lt;br /&gt;
if (!empty($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
    echo price($line-&amp;gt;total_ht * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient);&lt;br /&gt;
} else {&lt;br /&gt;
    echo price($line-&amp;gt;total_ht);&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should produce something like this:&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Technical note: here we are using customfields_fill_object() instead of customfields_fill_object_lines() (the latter being specifically adapted to lines and thus should theoretically be used here) for performance reasons because the template file we are editing is called in a loop, and customfields_fill_object_lines() should be called outside a loop (because it already does the job that is done by the loop), so here we &#039;tricks&#039; the customfields_fill_object() function to avoid this loop by submitting &#039;facturedet&#039; as a module by itself (when in fact it&#039;s a product lines table, but it works the same way nevertheless). This is a good demonstration of the flexibility of the CustomFields functions and methods.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the coefficient column in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
It&#039;s also possible to show the coefficient in a new column on the Dolibarr interface.&lt;br /&gt;
&lt;br /&gt;
To do that, you need to do two things:&lt;br /&gt;
&lt;br /&gt;
1- Add the column title &amp;quot;Coeff.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
2- Add the generator that will input the coefficient value for each line.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Add the column title &amp;quot;Coeff.&amp;quot;&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
Open in your favourite editor the file /htdocs/core/class/commonobject.class.php and inside the function printObjectLines (note the plural s), find the following lines of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Total HT&lt;br /&gt;
print &#039;&amp;lt;td align=&amp;quot;right&amp;quot; width=&amp;quot;50&amp;quot;&amp;gt;&#039;.$langs-&amp;gt;trans(&#039;TotalHTShort&#039;).&#039;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is what prints the Total HT price column. We will place the Coeff column just before.&lt;br /&gt;
&lt;br /&gt;
So, just above this code, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
dol_include_once(&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element_line);&lt;br /&gt;
$coeff_field = $customfields-&amp;gt;fetchFieldStruct(&#039;coefficient&#039;);&lt;br /&gt;
if (!empty($coeff_field)) {&lt;br /&gt;
    print &#039;&amp;lt;td align=&amp;quot;right&amp;quot; width=&amp;quot;50&amp;quot;&amp;gt;&#039;.$langs-&amp;gt;trans(&#039;Coeff.&#039;).&#039;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will take care of showing the Coeff. column, but only for modules where you created such a custom field (because the file we modified is a common object for all modules in Dolibarr, not just invoices, thus this modification will gracefully skip this custom field if it does not exists for other modules).&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Add the generator&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/tpl/objectline_view.tpl.php and try to find the following block of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php if ($line-&amp;gt;special_code == 3)	{ ?&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo $langs-&amp;gt;trans(&#039;Option&#039;); ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } else { ?&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is what prints the total HT price value for each line.&lt;br /&gt;
&lt;br /&gt;
Just above of this block of code, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $object-&amp;gt;table_element_line; // you can use $this-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
if (isset($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
    &amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient; ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just like for the column title, this code will take care of generating the coefficient value only if the custom field is defined, because this file is also a generic template for all modules of Dolibarr.&lt;br /&gt;
&lt;br /&gt;
At this point, everything is done, you should see the following:&lt;br /&gt;
[[File:Cf_wiki_coeff_column.png]]&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in margin module ===&lt;br /&gt;
&lt;br /&gt;
If you enabled the margin module, you should see that the coefficient is not applied there, but it&#039;s easy to fix that.&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/class/commonobject.class.php and find the function getMarginInfos(), then inside find the following lines:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach($this-&amp;gt;lines as $line) {&lt;br /&gt;
    if (empty($line-&amp;gt;pa_ht) &amp;amp;&amp;amp; isset($line-&amp;gt;fk_fournprice) &amp;amp;&amp;amp; !$force_price) {&lt;br /&gt;
        $product = new ProductFournisseur($this-&amp;gt;db);&lt;br /&gt;
        if ($product-&amp;gt;fetch_product_fournisseur_price($line-&amp;gt;fk_fournprice))&lt;br /&gt;
            $line-&amp;gt;pa_ht = $product-&amp;gt;fourn_unitprice * (1 - $product-&amp;gt;fourn_remise_percent / 100);&lt;br /&gt;
        if (isset($conf-&amp;gt;global-&amp;gt;MARGIN_TYPE) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;MARGIN_TYPE == &amp;quot;2&amp;quot; &amp;amp;&amp;amp; $product-&amp;gt;fourn_unitcharges &amp;gt; 0)&lt;br /&gt;
            $line-&amp;gt;pa_ht += $product-&amp;gt;fourn_unitcharges;&lt;br /&gt;
    }&lt;br /&gt;
    // si prix d&#039;achat non renseigné et devrait l&#039;être, alors prix achat = prix vente&lt;br /&gt;
    if ((!isset($line-&amp;gt;pa_ht) || $line-&amp;gt;pa_ht == 0) &amp;amp;&amp;amp; $line-&amp;gt;subprice &amp;gt; 0 &amp;amp;&amp;amp; (isset($conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull == 1)) {&lt;br /&gt;
        $line-&amp;gt;pa_ht = $line-&amp;gt;subprice * (1 - ($line-&amp;gt;remise_percent / 100));&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the lines are filled with margin infos. The price has just been fetched here, and we will now modify it with the coefficient before the price gets used for margin computation.&lt;br /&gt;
&lt;br /&gt;
Just below the code we found, paste the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $line-&amp;gt;table_element;&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
if (!empty($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
    $line-&amp;gt;pa_ht = $line-&amp;gt;pa_ht * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient;&lt;br /&gt;
    $line-&amp;gt;subprice = $line-&amp;gt;subprice * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s all, the price should be reflected in the margin.&lt;br /&gt;
[[File:Cf_wiki_coeff_margin.png]]&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in remaining debt to pay ===&lt;br /&gt;
&lt;br /&gt;
Open /htdocs/core/class/commonobject.class.php and look for the function update_price().&lt;br /&gt;
&lt;br /&gt;
You should see the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function update_price($exclspec=0,$roundingadjust=&#039;none&#039;,$nodatabaseupdate=0,$seller=&#039;&#039;)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just below the include_once line, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;;&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/conf/conf_customfields_func.lib.php&#039;;&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $this-&amp;gt;table_element_line);&lt;br /&gt;
// Fetching line ids from parent object id&lt;br /&gt;
$line_ids = $customfields-&amp;gt;fetchAny(&#039;rowid&#039;, MAIN_DB_PREFIX.$this-&amp;gt;table_element_line, $this-&amp;gt;fk_element.&#039; = &#039;.$this-&amp;gt;id);&lt;br /&gt;
$line_ids = array_values_recursive(&#039;rowid&#039;, $line_ids);&lt;br /&gt;
// Load custom fields&lt;br /&gt;
$cf_records = $customfields-&amp;gt;fetch($line_ids);&lt;br /&gt;
// Reassociate the records with the line id (instead of the customfields record id)&lt;br /&gt;
$cf_line_cfrowid_to_rowid = array_reassociate(&#039;rowid&#039;, &#039;fk_&#039;.$this-&amp;gt;table_element_line, $cf_records);&lt;br /&gt;
$tmp = $cf_records;&lt;br /&gt;
$cf_records = array();&lt;br /&gt;
foreach ($tmp as $k=&amp;gt;$v) { $cf_records[$cf_line_cfrowid_to_rowid[$k]] = $tmp[$k]; }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will load the coefficient custom field (and also any other custom field you have defined for lines).&lt;br /&gt;
&lt;br /&gt;
Now scroll below, and you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
while ($i &amp;lt; $num)&lt;br /&gt;
{&lt;br /&gt;
    $obj = $this-&amp;gt;db-&amp;gt;fetch_object($resql);&lt;br /&gt;
&lt;br /&gt;
    // Note: There is no check on detail line and no check on total, if $forcedroundingmode = &#039;none&#039;&lt;br /&gt;
&lt;br /&gt;
    if ($forcedroundingmode == &#039;0&#039;)	// Check if data on line are consistent. This may solve lines that were not consistent because set with $forcedroundingmode=&#039;auto&#039;&lt;br /&gt;
    {&lt;br /&gt;
        $localtax_array=array($obj-&amp;gt;localtax1_type,$obj-&amp;gt;localtax1_tx,$obj-&amp;gt;localtax2_type,$obj-&amp;gt;localtax2_tx);&lt;br /&gt;
        $tmpcal=calcul_price_total($obj-&amp;gt;qty, $obj-&amp;gt;up, $obj-&amp;gt;remise_percent, $obj-&amp;gt;vatrate, $obj-&amp;gt;localtax1_tx, $obj-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $obj-&amp;gt;info_bits, $obj-&amp;gt;product_type, $seller, $localtax_array);&lt;br /&gt;
        $diff=price2num($tmpcal[1] - $obj-&amp;gt;total_tva, &#039;MT&#039;, 1);&lt;br /&gt;
        if ($diff)&lt;br /&gt;
        {&lt;br /&gt;
            $sqlfix=&amp;quot;UPDATE &amp;quot;.MAIN_DB_PREFIX.$this-&amp;gt;table_element_line.&amp;quot; SET &amp;quot;.$fieldtva.&amp;quot; = &amp;quot;.$tmpcal[1].&amp;quot;, total_ttc = &amp;quot;.$tmpcal[2].&amp;quot; WHERE rowid = &amp;quot;.$obj-&amp;gt;rowid;&lt;br /&gt;
            dol_syslog(&#039;We found unconsistent data into detailed line (difference of &#039;.$diff.&#039;) for line rowid = &#039;.$obj-&amp;gt;rowid.&amp;quot; (total vat of line calculated=&amp;quot;.$tmpcal[1].&amp;quot;, database=&amp;quot;.$obj-&amp;gt;total_tva.&amp;quot;). We fix the total_vat and total_ttc of line by running sqlfix = &amp;quot;.$sqlfix);&lt;br /&gt;
            $resqlfix=$this-&amp;gt;db-&amp;gt;query($sqlfix);&lt;br /&gt;
            if (! $resqlfix) dol_print_error($this-&amp;gt;db,&#039;Failed to update line&#039;);&lt;br /&gt;
            $obj-&amp;gt;total_tva = $tmpcal[1];&lt;br /&gt;
            $obj-&amp;gt;total_ttc = $tmpcal[2];&lt;br /&gt;
            //&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just after, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
$cf_coeff = $cf_records[$obj-&amp;gt;rowid]-&amp;gt;coefficient;&lt;br /&gt;
if (empty($cf_coeff) and $cf_coeff !== 0) $cf_coeff = 1; // by default, an empty coefficient = 1&lt;br /&gt;
$obj-&amp;gt;total_ht = $obj-&amp;gt;total_ht * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_tva = $obj-&amp;gt;total_tva * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_localtax1 = $obj-&amp;gt;total_localtax1 * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_localtax2 = $obj-&amp;gt;total_localtax2 * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_ttc = $obj-&amp;gt;total_ttc * $cf_coeff;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should do the trick. Now go to one of your invoices (or whatever module you are targetting), and try to add/edit a product/service line to force refresh the total price. You should then see this:&lt;br /&gt;
[[File:Cf_wiki_coeff_remaining_debt.png]]&lt;br /&gt;
&lt;br /&gt;
= Custom tax per product =&lt;br /&gt;
&lt;br /&gt;
This part will show you how to create a static custom tax per product. Static means that the tax will be stored per product, and not per product&#039;s line, thus you just have to setup the tax once per product and it will always be applied in every invoice you add this product.&lt;br /&gt;
&lt;br /&gt;
To do that, you will need the CustomFields Pro module + ProductsEasyAccess.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
1- Create a product&#039;s custom fields called &amp;quot;custom_tax&amp;quot;. Now, using ProductsEasyAccess, you can access this field wherever you wants, either to show it on the Dolibarr&#039;s interface (see previous chapter), or in a PDF template, which we will describe below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2- Modify your invoice&#039;s PDF template. You first need to load all the products&#039; fields &#039;&#039;before&#039;&#039; the loop over the products&#039; lines. To do this, search for a line like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// This should be placed above the &amp;quot;loop lines&amp;quot; below, add this:&lt;br /&gt;
&lt;br /&gt;
dol_include_once(&#039;/productseasyaccess/lib/productseasyaccess.lib.php&#039;);&lt;br /&gt;
fill_object_with_products_fields($object);&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines, search for this command&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  // Add this just below the for loop, this will ease your php manipulation after.&lt;br /&gt;
  // This will allow you to more easily access the products&#039; fields (this will store the id of each product inside $lineproductid).&lt;br /&gt;
  $line_product_id = $object-&amp;gt;lines[$i]-&amp;gt;fk_product;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4- You can now use your products&#039; fields in your PDF. To access the custom_tax field, just use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And to print it in your PDF:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell(0,3, $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax, 0, &#039;L&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5- Then, if you want to count the custom tax total and add it to your vat total, you can create a counter inside the for loop we cited in the first step:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_custom_tax = 0; // Initialize the custom tax total counter&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  $object-&amp;gt;total_custom_tax += $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then find inside the function **_tableau_tot**, find the following line:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And prepend the total modification like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc + $object-&amp;gt;total_custom_tax; // prepend this to add the custom tax to the total&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Done. This should show your custom tax per product and your total including all custom taxes.&lt;/div&gt;</summary>
		<author><name>Lrq3000</name></author>
	</entry>
	<entry>
		<id>https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33069</id>
		<title>Module CustomFields Cases</title>
		<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33069"/>
		<updated>2015-01-15T15:42:30Z</updated>

		<summary type="html">&lt;p&gt;Lrq3000: /* Implementing the change of price in remaining debt to pay */ Added infos about refreshing total price after code modification&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:CustomFields]]&lt;br /&gt;
{{TemplateDocDevEn}}&lt;br /&gt;
{{TemplateModEN}}&lt;br /&gt;
{{BasculeDevUserEn|&lt;br /&gt;
name=CustomFields|&lt;br /&gt;
num=8500|&lt;br /&gt;
devdoc=[[Module_CustomFields_Dev]]|&lt;br /&gt;
userdoc=[[Module_CustomFields]]|}}&lt;br /&gt;
This page lists a compilation of concrete, practical examples uses of [[Module_CustomFields|CustomFields]] and of its various features.&lt;br /&gt;
&lt;br /&gt;
If you had an experience with CustomFields, you can also add here your case as an example for others to get inspired.&lt;br /&gt;
&lt;br /&gt;
= How to make a generic PDF template listing all custom fields =&lt;br /&gt;
&lt;br /&gt;
To make a generic PDF template listing all custom fields, you just need to take an existing PDF template, then you need to follow these 3 steps:&lt;br /&gt;
&lt;br /&gt;
1/ Edit the class name (required, else it will conflict with the original template).&lt;br /&gt;
&lt;br /&gt;
Eg: if we take the crabe template for invoices, we change:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;crabe&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
into:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_customfields extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;customfields&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2/ Copy this generic function at the end of the PDF:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propale/order/etc... (CustomFields simpler functions will automatically adapt)&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs); // set default PDF font size&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
    // Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
    $customfields = customfields_fill_object($object, null, $outputlangs, null, true);&lt;br /&gt;
&lt;br /&gt;
    // Setting the starting position of the text cursor&lt;br /&gt;
    $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
    $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
    // Printing the customfields&lt;br /&gt;
    foreach ($object-&amp;gt;customfields as $label=&amp;gt;$value) { // $value is already formatted!&lt;br /&gt;
        // Get translated label&lt;br /&gt;
        $translatedlabel = $customfields-&amp;gt;findLabelPDF($label, $outputlangs); // translated label of the customfield (not translated by default in customfields_fill_object() because a field should always be accessible by a base name, whatever the translation is)&lt;br /&gt;
&lt;br /&gt;
        // PDF formatting, placement and printing&lt;br /&gt;
        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
        $pdf-&amp;gt;MultiCell(0,3, $translatedlabel.&#039; ($object-&amp;gt;customfields-&amp;gt;&#039;.$label.&#039;): &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternative function (with a bit less functionalities, but more controllable):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page (used to debug if CustomFields setup is correct)&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propal/product/whatever...&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs);&lt;br /&gt;
&lt;br /&gt;
            if (empty($object-&amp;gt;table_element) or empty($object-&amp;gt;id)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;Current \$object is not compatible with CustomFields, could not find table_element or id.&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
    $customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element);&lt;br /&gt;
&lt;br /&gt;
            // Fetching custom fields records&lt;br /&gt;
            $fields = $customfields-&amp;gt;fetch($object-&amp;gt;id);&lt;br /&gt;
&lt;br /&gt;
            if (!isset($fields)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;No custom field could be found for this object. Please check your configuration (did you create at least one customfield and set a value in the current datasheet?)&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            } else {&lt;br /&gt;
                // Setting the starting position of the text cursor&lt;br /&gt;
                $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
                $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
                // Printing the customfields&lt;br /&gt;
                foreach ($fields as $key=&amp;gt;$field) {&lt;br /&gt;
                        $translatedname = $customfields-&amp;gt;findLabelPDF($key, $outputlangs); // label of the customfield&lt;br /&gt;
                        $value = $customfields-&amp;gt;simpleprintFieldPDF($key, $field, $outputlangs); // value (cleaned and properly formatted) of the customfield&lt;br /&gt;
&lt;br /&gt;
                        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
                        $pdf-&amp;gt;MultiCell(0,3, $translatedname.&#039;: &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
                        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3/ Call the _pagecustomfields function at the right place (generally, after _pagefoot and just before $pdf-&amp;gt;Close()):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Pied de page&lt;br /&gt;
$this-&amp;gt;_pagefoot($pdf,$object,$outputlangs);&lt;br /&gt;
$pdf-&amp;gt;AliasNbPages();&lt;br /&gt;
&lt;br /&gt;
// CustomFields&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) { // if the customfields module is activated...&lt;br /&gt;
        // Add a page with the customfields&lt;br /&gt;
        $pdf-&amp;gt;AddPage();&lt;br /&gt;
        $this-&amp;gt;_pagecustomfields($pdf,$object,$outputlangs);&lt;br /&gt;
        $pagenb++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
$pdf-&amp;gt;Close(); // you should first search for this, you should only find one occurrence&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then go to the module&#039;s admin panel (eg: invoice admin panel) and enable the template you&#039;ve just created.&lt;br /&gt;
&lt;br /&gt;
You can now use it to test if your customfields work.&lt;br /&gt;
&lt;br /&gt;
= Conditions on fields selection =&lt;br /&gt;
&lt;br /&gt;
Sometimes you may want to show a DropdownBox or Constraint (or another multi-choice type), but you may want to show or hide choices based on conditions (eg: only if it&#039;s a prospect, only if this zone was selected, only if this category was selected, etc..).&lt;br /&gt;
&lt;br /&gt;
To know which is the best solution you should choose to implement your condition, you should first know in which category your condition falls in.&lt;br /&gt;
&lt;br /&gt;
== Conditions categories ==&lt;br /&gt;
&lt;br /&gt;
Conditions can be categorized into 4 types:&lt;br /&gt;
&lt;br /&gt;
* static condition: choices are restrained on a condition that never changes (eg: only show third-parties that are suppliers, or lowercase all data on sql insertion, or check that a field is above a certain number, etc.).&lt;br /&gt;
* semi-dynamic condition: choices that are constrained on a static table but based on the value of another field/customfield. This is also commonly called &amp;quot;Cascaded dropdown lists&amp;quot; or &amp;quot;Dynamically linked dropdown lists&amp;quot;.&lt;br /&gt;
* dynamic: everything is dynamic: choices are based on a subfield of a field of the current module, so that there&#039;s no material table and it&#039;s impossible to create a view (because you would need to create one view per id) (eg: show only the contacts attached to the third-party being accessed)&lt;br /&gt;
* timed condition: a condition that is based on time or recurrence (eg: at 5:00 AM delete old records, etc..)&lt;br /&gt;
&lt;br /&gt;
== Conditions solutions ==&lt;br /&gt;
&lt;br /&gt;
Now that you know what kind of condition you want to set, you can choose one of the following solutions depending on your preferences and knowledge.&lt;br /&gt;
&lt;br /&gt;
In any case, you can always use the &#039;&#039;&#039;CustomFields&#039;s overload functions&#039;&#039;&#039; to fit your needs for nearly every type of condition.&lt;br /&gt;
&lt;br /&gt;
=== Static condition ===&lt;br /&gt;
&lt;br /&gt;
* View: create a view on the table based on the condition, and then create a custom field constrained on this view (which is just like any table), eg: only show third-parties that are suppliers:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
CREATE VIEW llx_societe_supplier_view AS&lt;br /&gt;
SELECT *&lt;br /&gt;
FROM llx_societe&lt;br /&gt;
WHERE fournisseur=1;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
WARNING: foreign keys (and thus constraints) can&#039;t be used on views with MySQL, and materialized views neither exist, so you can&#039;t use this method with MySQL.&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
* SQL Transformations&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
=== Semi-dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade option is ideal in most cases, and if not enough, you can use the Cascade Custom option and Overloading Functions.&lt;br /&gt;
* View&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
&lt;br /&gt;
=== Dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade Custom and Overload functions&lt;br /&gt;
* Making your own module, which calls the CustomFields class&lt;br /&gt;
&lt;br /&gt;
=== Timed condition ===&lt;br /&gt;
&lt;br /&gt;
* SQL scheduled events&lt;br /&gt;
* Cron job&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
= Overloading functions =&lt;br /&gt;
&lt;br /&gt;
Here are a few concrete use cases of overloading functions to achieve various goals.&lt;br /&gt;
&lt;br /&gt;
== Dynamic list selection based on another one ==&lt;br /&gt;
&lt;br /&gt;
The goal is to show two custom fields on the Third-Party module: one which gives the zone (secteur) of the third-party (Isere, Alpes du sud, Haute-Savoie...) and the other which gives relative to the zone the list of all the ski resorts (station_a) inside the selected zone.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will show how to manually manage this kind of requirements using [[Module_CustomFields#Overloading_functions:_Adding_your_own_management_code_for_custom_fields|overloading functions]], but in latest CustomFields releases, the same solution can be achieved automatically using the [[Module_CustomFields#Cascade|Cascade option]].&lt;br /&gt;
&lt;br /&gt;
We have one table &#039;&#039;&#039;llx_k_station&#039;&#039;&#039; (rowid, station (char50), secteur(char50)) which contains the following: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1,les 2 alpes,isère&lt;br /&gt;
2,chamrousse,isère&lt;br /&gt;
3,alpe d&#039;huez,isère&lt;br /&gt;
4,vars,alpes du sud&lt;br /&gt;
5,risoul,alpes du sud&lt;br /&gt;
etc...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In CustomFields&#039;s admin panel, we create two custom fields:&lt;br /&gt;
&lt;br /&gt;
1- secteur: returns the list of all zones - type DropdownBox: enum(&#039;- Aucun&#039;,&#039;Alpes du Sud&#039;,&#039;export&#039;,&#039;Haute-Savoie&#039;,&#039;Isère&#039;, etc...)&lt;br /&gt;
&lt;br /&gt;
2- station_a: returns the list of all ski resorts - type constraint on &#039;&#039;&#039;llx_k_station&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Here is the code to put in customfields_fields_extend.lib.php that will allow to show only the ski resorts corresponding to the selected zone:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_editview_societe_station_a (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$parameters, &amp;amp;$action, &amp;amp;$user, &amp;amp;$idvar, &amp;amp;$rightok, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
  global $db; // allow to access database functions&lt;br /&gt;
  $sql=&amp;quot;SELECT llx_k_station.station, llx_k_station.rowid FROM llx_societe_customfields INNER JOIN llx_k_station ON llx_societe_customfields.Secteur = llx_k_station.secteur WHERE llx_societe_customfields.fk_societe=&amp;quot;.$object-&amp;gt;id;&lt;br /&gt;
$result=$db-&amp;gt;query($sql);&lt;br /&gt;
 &lt;br /&gt;
  if ($result) {&lt;br /&gt;
    $num = $db-&amp;gt;num_rows($result);&lt;br /&gt;
    $i = 0;&lt;br /&gt;
    $myarray = array();&lt;br /&gt;
    while ($i &amp;lt; $num)&lt;br /&gt;
    {&lt;br /&gt;
      $obj = $db-&amp;gt;fetch_object($result);&lt;br /&gt;
      $myarray []=array(&#039;id&#039;=&amp;gt;$obj-&amp;gt;rowid, &#039;value&#039;=&amp;gt;$obj-&amp;gt;station);&lt;br /&gt;
      $i++;&lt;br /&gt;
    }&lt;br /&gt;
    $value = $myarray; &lt;br /&gt;
    $db-&amp;gt;free($result);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to manub for giving this case.&lt;br /&gt;
&lt;br /&gt;
== Dynamic discount computation per products lines on propales ==&lt;br /&gt;
&lt;br /&gt;
The goal here is to compute the discount for a product line in propales based on a computation on three custom fields.&lt;br /&gt;
&lt;br /&gt;
First, create three custom fields for &amp;quot;Line commercial proposals&amp;quot;: vl, cpl and lc.&lt;br /&gt;
&lt;br /&gt;
Then add the following overloading function at the end of your customfields_fields_extend.lib.php file (read the comments for more informations):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_propaldet_vl (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
&lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $vl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;vl&#039;};&lt;br /&gt;
    $cpl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;cpl&#039;};&lt;br /&gt;
    $lc = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;lc&#039;};&lt;br /&gt;
&lt;br /&gt;
    // Compute the discount&lt;br /&gt;
    $remise_calculee = (100 - $vl) * (100 - $cpl) * (100 - $lc);&lt;br /&gt;
&lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $remise_calculee = price2num($remise_calculee);&lt;br /&gt;
&lt;br /&gt;
    // Assign the new discount into the PropalLine object&lt;br /&gt;
    $object-&amp;gt;remise_percent = $remise_calculee;&lt;br /&gt;
&lt;br /&gt;
    // Update the price (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
&lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
&lt;br /&gt;
    //$q = &amp;quot;UPDATE  &amp;quot;.MAIN_DB_PREFIX.&amp;quot;propaldet SET remise_percent=&amp;quot;.$remise_calculee.&amp;quot; WHERE  rowid=&amp;quot;.$object-&amp;gt;rowid;&lt;br /&gt;
    //$db-&amp;gt;query($q);&lt;br /&gt;
    &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;vl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;cpl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;lc&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Linking Dolibarr objects from two different modules =&lt;br /&gt;
&lt;br /&gt;
Dolibarr sometimes offers the possibility to link two different modules. Eg: when you convert an Intervention card into an Invoice.&lt;br /&gt;
&lt;br /&gt;
In this case, you might want in your templates to fetch the custom fields of the linked object. Eg: you generate a PDF from the Invoice, but you want to get the custom fields from the Intervention that was converted into the Invoice.&lt;br /&gt;
&lt;br /&gt;
The key here is to use the &#039;&#039;&#039;llx_element_element&#039;&#039;&#039; table or the &#039;&#039;&#039;llx_element_*&#039;&#039;&#039; tables (eg: llx_element_contact): this is a standard Dolibarr table that stores all links between objects of two different modules.&lt;br /&gt;
&lt;br /&gt;
Here is the structure:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
rowid | fk_source | sourcetype | fk_target | targettype&lt;br /&gt;
-------------------------------------------------------&lt;br /&gt;
1 | 2 | fichinter | 5 | facture&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can then use a php code like the following to populate your $object with customfields:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
// This will also create and returns us an instance of the CustomFields class, which we will use to manually query the llx_element_element table&lt;br /&gt;
$customfields = customfields_fill_object($object, null, $outputlangs, &#039;facture&#039;, true); // beautified values&lt;br /&gt;
&lt;br /&gt;
// Querying the llx_element_element table manually to get the link between the invoice and the linked intervention card (we want the id of the intervention card)&lt;br /&gt;
// fetchAny($columns, $table, $where=&#039;&#039;, $orderby=&#039;&#039;, $limitby=&#039;&#039;)&lt;br /&gt;
$eltrow = $customfields-&amp;gt;fetchAny(&#039;*&#039;, MAIN_DB_PREFIX.&#039;element_element&#039;, &#039;targettype=&amp;quot;&#039;.$object-&amp;gt;element.&#039;&amp;quot; and fk_target=&#039;.$object-&amp;gt;id.&#039; and sourcetype=&amp;quot;fichinter&amp;quot;&#039;);&lt;br /&gt;
&lt;br /&gt;
// Save the intervention&#039;s id&lt;br /&gt;
$sourceid = $eltrow[0][&#039;fk_source&#039;];&lt;br /&gt;
&lt;br /&gt;
// Crafting a dummy intervention object&lt;br /&gt;
$fromobject = new stdClass(); // Init an empty object&lt;br /&gt;
$fromobject-&amp;gt;id = $sourceid; // We need the id and..&lt;br /&gt;
$fromobject-&amp;gt;table_element = &#039;fichinter&#039;; // the table_element (name of the table)&lt;br /&gt;
&lt;br /&gt;
// We can then fill $object with the intervention custom fields (we store them in a sub property &#039;fichinter&#039; to avoid conflicts with invoice&#039;s custom fields)&lt;br /&gt;
customfields_fill_object($object, $fromobject, $outputlangs, &#039;fichinter&#039;, true);&lt;br /&gt;
&lt;br /&gt;
// Now you can access invoices custom fields using:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;facture-&amp;gt;cf_myfield);&lt;br /&gt;
// And access intervention custom fields with:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;fichinter-&amp;gt;cf_myfield);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to netassopro for the tip.&lt;br /&gt;
&lt;br /&gt;
= Modify the total global price with a coefficient (without tampering the discount) =&lt;br /&gt;
&lt;br /&gt;
This is more troublesome than modifying the total price per product/service line, because the total global price of an object (let&#039;s say an invoice, but this also works for any other object) is always recomputed by summing the prices and quantity of each product/service line. This means that although there&#039;s an entry &amp;quot;total_ttc&amp;quot; in the database to store the invoice&#039;s total price, this price is in fact always recomputed, it&#039;s never used as-is. Thus, it&#039;s useless to change only the database record: we need to inject some code to change how the total price is computed.&lt;br /&gt;
&lt;br /&gt;
This could be done using an overloading function or a trigger, but unluckily, Dolibarr doesn&#039;t call a trigger nor a hook when recomputing the price. We thus need to edit core files to do what we want.&lt;br /&gt;
&lt;br /&gt;
However, the bright side is that we can cleverly edit just two functions from one core file: update_price() and getMarginInfos() from /htdocs/core/class/commonobject.class.php , and everything should run smoothly. Beware that if you update Dolibarr, you will need to redo these two modifications in the code.&lt;br /&gt;
&lt;br /&gt;
First, you need to create a new custom field called &amp;quot;global_coefficient&amp;quot; in Invoices (not Invoices Lines!). Then you can continue to the subchapters below.&lt;br /&gt;
&lt;br /&gt;
Also note that this global coefficient is also compatible with per line coefficients (as described in the next chapter), thus you can use a global coefficient and per line coefficients at the same time. You can also use multiple global coefficient if you wish.&lt;br /&gt;
&lt;br /&gt;
Another note: if you have a grid of discounts depending on quantity or price or whatever parameter, you can program a precomputation code using a Custom Ajax function (this way, your discounts will be automatically computed, but then you can still manually enter them for particular cases since the custom field is editable).&lt;br /&gt;
&lt;br /&gt;
== Implement the change of global price in most of Dolibarr (including remaining debt to pay and PDF and ODT) ==&lt;br /&gt;
&lt;br /&gt;
The change we will implement here will reflect the new total price with the global coefficient in most of Dolibarr&#039;s code, since we will here change the main function that is usually called to update and get the total price.&lt;br /&gt;
&lt;br /&gt;
Open /htdocs/core/class/commonobject.class.php and look for the function update_price().&lt;br /&gt;
&lt;br /&gt;
You should see the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function update_price($exclspec=0,$roundingadjust=&#039;none&#039;,$nodatabaseupdate=0,$seller=&#039;&#039;)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just below the include_once line, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;;&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/conf/conf_customfields_func.lib.php&#039;;&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $this-&amp;gt;table_element);&lt;br /&gt;
// Get object&#039;s id (either stored in property &amp;quot;id&amp;quot; or &amp;quot;rowid&amp;quot;)&lt;br /&gt;
$this_id = (!empty($this-&amp;gt;id)) ? $this-&amp;gt;id : $this-&amp;gt;rowid;&lt;br /&gt;
// Load custom fields records&lt;br /&gt;
$cf_parent_record = $customfields-&amp;gt;fetch($this_id);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will load the global_coefficient custom field (and also any other custom field you have defined for invoices).&lt;br /&gt;
&lt;br /&gt;
Now scroll below, and you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
while ($i &amp;lt; $num)&lt;br /&gt;
{&lt;br /&gt;
    $obj = $this-&amp;gt;db-&amp;gt;fetch_object($resql);&lt;br /&gt;
&lt;br /&gt;
    ... (lots of code)&lt;br /&gt;
&lt;br /&gt;
    i++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Add revenue stamp to total&lt;br /&gt;
$this-&amp;gt;total_ttc       += isset($this-&amp;gt;revenuestamp)?$this-&amp;gt;revenuestamp:0;&lt;br /&gt;
&lt;br /&gt;
$this-&amp;gt;db-&amp;gt;free($resql);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is the end of the loop that is summing each product/service line price to compute the total price. Just after this loop, you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Now update global field total_ht, total_ttc and tva&lt;br /&gt;
$fieldht=&#039;total_ht&#039;;&lt;br /&gt;
$fieldtva=&#039;tva&#039;;&lt;br /&gt;
$fieldlocaltax1=&#039;localtax1&#039;;&lt;br /&gt;
$fieldlocaltax2=&#039;localtax2&#039;;&lt;br /&gt;
$fieldttc=&#039;total_ttc&#039;;&lt;br /&gt;
// Specific code for backward compatibility with old field names&lt;br /&gt;
if ($this-&amp;gt;element == &#039;facture&#039; || $this-&amp;gt;element == &#039;facturerec&#039;)             $fieldht=&#039;total&#039;;&lt;br /&gt;
if ($this-&amp;gt;element == &#039;facture_fourn&#039; || $this-&amp;gt;element == &#039;invoice_supplier&#039;) $fieldtva=&#039;total_tva&#039;;&lt;br /&gt;
if ($this-&amp;gt;element == &#039;propal&#039;)                                                $fieldttc=&#039;total&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Between those two blocks of code (so just above // Now update global field total_ht, total_ttc and tva), you can place the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
if (!empty($cf_parent_record-&amp;gt;global_coefficient)) { // avoid applying the coefficient if empty (this will return a 0 price)&lt;br /&gt;
    $this-&amp;gt;total_ht = $this-&amp;gt;total_ht * $cf_parent_record-&amp;gt;global_coefficient;&lt;br /&gt;
    $this-&amp;gt;total_tva = $this-&amp;gt;total_tva * $cf_parent_record-&amp;gt;global_coefficient;&lt;br /&gt;
    $this-&amp;gt;total_ttc = $this-&amp;gt;total_ttc * $cf_parent_record-&amp;gt;global_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Done, now try to add/edit a product/service line, and the total price should be affected by the global coefficient.&lt;br /&gt;
&lt;br /&gt;
== Implementing the change of price in margin module ==&lt;br /&gt;
&lt;br /&gt;
If you enabled the margin module, you should see that the coefficient is not applied there, but it&#039;s easy to fix that.&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/class/commonobject.class.php and find the function getMarginInfos(), then inside find the following lines:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach($this-&amp;gt;lines as $line) {&lt;br /&gt;
    if (empty($line-&amp;gt;pa_ht) &amp;amp;&amp;amp; isset($line-&amp;gt;fk_fournprice) &amp;amp;&amp;amp; !$force_price) {&lt;br /&gt;
        $product = new ProductFournisseur($this-&amp;gt;db);&lt;br /&gt;
        if ($product-&amp;gt;fetch_product_fournisseur_price($line-&amp;gt;fk_fournprice))&lt;br /&gt;
            $line-&amp;gt;pa_ht = $product-&amp;gt;fourn_unitprice * (1 - $product-&amp;gt;fourn_remise_percent / 100);&lt;br /&gt;
        if (isset($conf-&amp;gt;global-&amp;gt;MARGIN_TYPE) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;MARGIN_TYPE == &amp;quot;2&amp;quot; &amp;amp;&amp;amp; $product-&amp;gt;fourn_unitcharges &amp;gt; 0)&lt;br /&gt;
            $line-&amp;gt;pa_ht += $product-&amp;gt;fourn_unitcharges;&lt;br /&gt;
    }&lt;br /&gt;
    // si prix d&#039;achat non renseigné et devrait l&#039;être, alors prix achat = prix vente&lt;br /&gt;
    if ((!isset($line-&amp;gt;pa_ht) || $line-&amp;gt;pa_ht == 0) &amp;amp;&amp;amp; $line-&amp;gt;subprice &amp;gt; 0 &amp;amp;&amp;amp; (isset($conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull == 1)) {&lt;br /&gt;
        $line-&amp;gt;pa_ht = $line-&amp;gt;subprice * (1 - ($line-&amp;gt;remise_percent / 100));&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the lines are filled with margin infos. The price has just been fetched here, and we will now modify it with the coefficient before the price gets used for margin computation.&lt;br /&gt;
&lt;br /&gt;
Just below the code we found, paste the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
// Get object&#039;s id (either stored in property &amp;quot;id&amp;quot; or &amp;quot;rowid&amp;quot;)&lt;br /&gt;
$this_id = (!empty($this-&amp;gt;id)) ? $this-&amp;gt;id : $this-&amp;gt;rowid;&lt;br /&gt;
// Load custom fields records&lt;br /&gt;
$customfields = customfields_fill_object($this);&lt;br /&gt;
// Apply the coefficient&lt;br /&gt;
if (!empty($this-&amp;gt;customfields-&amp;gt;cf_global_coefficient)) {&lt;br /&gt;
    $line-&amp;gt;pa_ht = $line-&amp;gt;pa_ht * $this-&amp;gt;customfields-&amp;gt;cf_global_coefficient;&lt;br /&gt;
    $line-&amp;gt;subprice = $line-&amp;gt;subprice * $this-&amp;gt;customfields-&amp;gt;cf_global_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that here we cheated a bit: we do not compute the coefficient on the global total price, but rather per product/service line price. This is done on purpose, because mathematically this should produce no difference, and it also allows Dolibarr to continue peacefully with its margin calculations (else we would have to adapt all those computations, which would imply a lot more of code changes!).&lt;br /&gt;
&lt;br /&gt;
That&#039;s all, the price should be reflected in the margin. Just refresh the page to reflect the change.&lt;br /&gt;
&lt;br /&gt;
= Modify the total price PER product/service line with a coefficient (without tampering the discount) =&lt;br /&gt;
&lt;br /&gt;
Here we will go through the creation and management of a custom field called &#039;&#039;&#039;coefficient&#039;&#039;&#039; that will change the total price of each product by modifying it by the coefficient.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will not create a coefficient per product but rather per product&#039;s line. Thus if you want to create a coefficient that never change between invoices but only depending on the product and stays static, please refer to the next chapter below about creating a custom tax per product.&lt;br /&gt;
&lt;br /&gt;
The preliminary and necessary step is to first create a custom field that will be used to modify the total price. We will create one for Client Invoice Lines.&lt;br /&gt;
&lt;br /&gt;
Go to the CustomFields administration panel, then into the Client Invoice Lines tab, then click on the button New Field, name the field &#039;&#039;&#039;coefficient&#039;&#039;&#039;, and set the type to &#039;&#039;&#039;Double&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
When you go to client invoices in Dolibarr, you should then see something similar to this:&lt;br /&gt;
[[File:Cfe1.jpg]]&lt;br /&gt;
You can see that our coefficient field was successfully created.&lt;br /&gt;
&lt;br /&gt;
Then here are two alternative ways of implementing the change of the total price of each product relativery to this coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change total price in database ==&lt;br /&gt;
&lt;br /&gt;
Here we will use a &amp;quot;save&amp;quot; overloading function to change the total price directly in the database, thus we won&#039;t have to modify anything else (eg: nor PDF templating nor ODT) since the total price will be already updated in the database.&lt;br /&gt;
&lt;br /&gt;
Warning: this method will work only with CustomFields =&amp;gt; 3.2.23 since the &amp;quot;save&amp;quot; overloading function did not work for products lines before.&lt;br /&gt;
&lt;br /&gt;
Just add the following overloading function in your customfields_fields_extend.lib.php file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_facturedet_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
 &lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $coefficient = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;coefficient&#039;}; // you could also use $coefficient = $value;&lt;br /&gt;
 &lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $coefficient = price2num($coefficient);&lt;br /&gt;
 &lt;br /&gt;
    // Get the original subprices (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
 &lt;br /&gt;
    // Update the subprices with the coefficient&lt;br /&gt;
    $object-&amp;gt;total_ht = $object-&amp;gt;total_ht * $coefficient; // compute no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva = $object-&amp;gt;total_tva * $coefficient; // compute tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc * $coefficient; // compute tax included price&lt;br /&gt;
 &lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(&#039;&#039;,true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
 &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;coefficient&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should show the correct total price on the Dolibarr&#039;s interface without modifying the unit subprice (P.U. Unit):&lt;br /&gt;
&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
You have nothing to edit in your PDF or ODT template, since the new subprice with applied coefficient will be automatically printed instead of the old one without coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change printed total price per product without tampering the subprice in the database ==&lt;br /&gt;
&lt;br /&gt;
Here is a way to change the printed total price (the one shown on screen and in PDF/ODT) but without changing the total price in the database.&lt;br /&gt;
&lt;br /&gt;
The following sub-chapters are all independent, thus you can only follow the ones that you require, eg: if you only need to change the price in your PDF documents, just create a custom field then jump to the sub-chapter to &#039;&#039;&#039;Implement the change of price in PDF&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in ODT templates ===&lt;br /&gt;
&lt;br /&gt;
There&#039;s no eval implementation currently inside ODT templates, but there is a kind of hook implementation, called [[Create_an_ODT_document_template#Other_personalized_tags|substitution functions]].&lt;br /&gt;
&lt;br /&gt;
You can either create your own module, then create your own substitution function, but for the sake of simplicity in this tutorial, we will directly use the CustomFields substitution function. However, please remember that you will have to redo your changes everytime you will update the CustomFields module, so if you don&#039;t want to do that it would be better if you create your own module and substitution function.&lt;br /&gt;
&lt;br /&gt;
Open the file &#039;&#039;&#039;htdocs/customfields/core/substitutions/functions_customfields.lib.php&#039;&#039;&#039; and go to the function &#039;&#039;&#039;customfields_completesubstitutionarray_lines&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
At the very end of this function, but inside the customfields check:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) {&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
But just before the ending bracket, you should add the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($object-&amp;gt;table_element_line === &#039;facturedet&#039;) { // First check that we are in the right module, we don&#039;t want to use coefficient for other modules if we didn&#039;t define the custom field there&lt;br /&gt;
    $substitutionarray[&#039;line_price_full&#039;] = $substitutionarray[&#039;line_price_ttc&#039;] * $substitutionarray[&#039;cf_coefficient&#039;]; // multiply the total price with our coefficient&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code will add a new tag &#039;&#039;&#039;{line_price_full}&#039;&#039;&#039; that you can then use inside your ODT templates.&lt;br /&gt;
&lt;br /&gt;
Just place this tag in a table in your ODT document, between the table tags ( [!-- BEGIN row.lines --] ... [!-- END row.lines --] ), and this will print your full price.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in PDF templates ===&lt;br /&gt;
&lt;br /&gt;
First, we will create our own PDF template by copying an existing template, and then we will edit it to change the price of items.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Creating your own PDF template by copying an existing template&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Go into the following folder: &#039;&#039;&#039;htdocs/core/modules/facture/doc/&#039;&#039;&#039;, then copy the file &#039;&#039;&#039;pdf_crabe.modules.php&#039;&#039;&#039; and name the new one &#039;&#039;&#039;pdf_crabecoeff.modules.php&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Then open it in your favourite editor, and change the following lines:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures // change pdf_crabe into pdf_crabecoeff&lt;br /&gt;
{&lt;br /&gt;
...&lt;br /&gt;
    function __construct($db)&lt;br /&gt;
    {&lt;br /&gt;
        ...&lt;br /&gt;
	$this-&amp;gt;name = &amp;quot;crabe&amp;quot;; // change &amp;quot;crabe&amp;quot; into &amp;quot;crabecoeff&amp;quot;&lt;br /&gt;
        ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you have your own PDF template!&lt;br /&gt;
&lt;br /&gt;
Now you should &#039;&#039;&#039;enable your PDF template&#039;&#039;&#039; before doing anything else, by going into the Modules Administration panel, and into the Invoice admin page, then click on the ON button at the right of the &#039;&#039;&#039;crabecoeff&#039;&#039;&#039; entry to enable the template.&lt;br /&gt;
&lt;br /&gt;
Now, let&#039;s proceed onto modifying the prices.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Changing the prices automatically in your PDF&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
You can now do pretty much anything you want in your PDF template, but I will give you a mean to automatically change the prices of every products with our coefficient, as well as the total price, without editing too much the template.&lt;br /&gt;
&lt;br /&gt;
First, try to find the following code of line (should be inside the write_file() method):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf=pdf_getInstance($this-&amp;gt;format);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, just copy/paste the following code &#039;&#039;&#039;below&#039;&#039;&#039; pdf_getInstance():&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$this-&amp;gt;customfields = customfields_fill_object($object, null, $outputlangs, null, true); // beautified values&lt;br /&gt;
$this-&amp;gt;customfields_raw = customfields_fill_object($object, null, $outputlangs, &#039;raw&#039;, null); // raw values&lt;br /&gt;
$this-&amp;gt;customfields_lines = customfields_fill_object_lines($object, null, $outputlangs, null, true); // product lines&#039; values&lt;br /&gt;
&lt;br /&gt;
// Resetting every $object price because we will compute them anew&lt;br /&gt;
$object-&amp;gt;total_ht = 0;&lt;br /&gt;
$object-&amp;gt;total_tva = 0;&lt;br /&gt;
$object-&amp;gt;total_ttc = 0;&lt;br /&gt;
// For each line of product/service, we will recompute all the prices&lt;br /&gt;
foreach($object-&amp;gt;lines as $line) {&lt;br /&gt;
    // Get the coefficient custom field&#039;s value for the current line&lt;br /&gt;
    $coeff = $object-&amp;gt;customfields-&amp;gt;lines-&amp;gt;{$line-&amp;gt;rowid}-&amp;gt;cf_coefficient;&lt;br /&gt;
    // Recompute the price of this product/service line&lt;br /&gt;
    $line-&amp;gt;total_ht = $line-&amp;gt;total_ht*$coeff; // compute no tax price&lt;br /&gt;
    $line-&amp;gt;total_tva = $line-&amp;gt;total_tva*$coeff; // compute tax amount&lt;br /&gt;
    $line-&amp;gt;total_ttc = $line-&amp;gt;total_ttc*$coeff; // compute tax included price&lt;br /&gt;
    // Do the sum of all new products/services prices and tva (will be incremented with every product/service line)&lt;br /&gt;
    $object-&amp;gt;total_ht += $line-&amp;gt;total_ht; // global no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva += $line-&amp;gt;total_tva; // global vat/tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc += $line-&amp;gt;total_ttc; // global  tax included price&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you should try to generate your PDF document, and you should get something like this:&lt;br /&gt;
[[File:Cfe3.jpg]]&lt;br /&gt;
&lt;br /&gt;
Note: printing the coefficient as a column in your PDF document is also possible but will not be covered here because PDF templates will vary for every persons depending on your needs.&lt;br /&gt;
For guidelines about how to do this, you can read the documentation on [[Create_a_PDF_document_template|how to create a PDF template]] and [[Module_CustomFields#Implementing_in_PDF_templates|how to use custom fields in PDF templates]].&lt;br /&gt;
&lt;br /&gt;
Hint: you should be looking inside the following conditional block, inside the write_file() method:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
{&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of total price in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
Unluckily there is not (yet) any hooking context to modify how lines of products/services are printed, thus the only thing you can do is directly edit the &#039;&#039;&#039;template file&#039;&#039;&#039; and manually put inside your code to load your custom field and print it the way you want using HTML formatting, for our example:&lt;br /&gt;
&lt;br /&gt;
* For Dolibarr 3.3 and above, edit the file &#039;&#039;&#039;htdocs/core/tpl/objectline_view.tpl.php&#039;&#039;&#039;&lt;br /&gt;
* For Dolibarr 3.1 and 3.2, edit the file &#039;&#039;&#039;htdocs/core/tpl/freeproductline_view.tpl.php&#039;&#039;&#039; AND &#039;&#039;&#039;predefinedproductline_view.tpl.php&#039;&#039;&#039; (apply the same changes to both files)&lt;br /&gt;
&lt;br /&gt;
In the template file you opened, find the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And replace it by:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $object-&amp;gt;table_element_line; // you can use $object-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
&lt;br /&gt;
// Modifying the HT price and printing it on the Dolibarr&#039;s datasheet interface&lt;br /&gt;
// Applying a multiplying/dividing coefficient to an HT price will give a mathematically correct TTC price since the taxes are also a multiplier&lt;br /&gt;
// However, if you want to apply other mathematical operations (like addition or substaction), you should use the total price with $line-&amp;gt;total_ttc&lt;br /&gt;
if (!empty($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
    echo price($line-&amp;gt;total_ht * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient);&lt;br /&gt;
} else {&lt;br /&gt;
    echo price($line-&amp;gt;total_ht);&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should produce something like this:&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Technical note: here we are using customfields_fill_object() instead of customfields_fill_object_lines() (the latter being specifically adapted to lines and thus should theoretically be used here) for performance reasons because the template file we are editing is called in a loop, and customfields_fill_object_lines() should be called outside a loop (because it already does the job that is done by the loop), so here we &#039;tricks&#039; the customfields_fill_object() function to avoid this loop by submitting &#039;facturedet&#039; as a module by itself (when in fact it&#039;s a product lines table, but it works the same way nevertheless). This is a good demonstration of the flexibility of the CustomFields functions and methods.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the coefficient column in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
It&#039;s also possible to show the coefficient in a new column on the Dolibarr interface.&lt;br /&gt;
&lt;br /&gt;
To do that, you need to do two things:&lt;br /&gt;
&lt;br /&gt;
1- Add the column title &amp;quot;Coeff.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
2- Add the generator that will input the coefficient value for each line.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Add the column title &amp;quot;Coeff.&amp;quot;&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
Open in your favourite editor the file /htdocs/core/class/commonobject.class.php and inside the function printObjectLines (note the plural s), find the following lines of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Total HT&lt;br /&gt;
print &#039;&amp;lt;td align=&amp;quot;right&amp;quot; width=&amp;quot;50&amp;quot;&amp;gt;&#039;.$langs-&amp;gt;trans(&#039;TotalHTShort&#039;).&#039;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is what prints the Total HT price column. We will place the Coeff column just before.&lt;br /&gt;
&lt;br /&gt;
So, just above this code, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
dol_include_once(&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element_line);&lt;br /&gt;
$coeff_field = $customfields-&amp;gt;fetchFieldStruct(&#039;coefficient&#039;);&lt;br /&gt;
if (!empty($coeff_field)) {&lt;br /&gt;
    print &#039;&amp;lt;td align=&amp;quot;right&amp;quot; width=&amp;quot;50&amp;quot;&amp;gt;&#039;.$langs-&amp;gt;trans(&#039;Coeff.&#039;).&#039;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will take care of showing the Coeff. column, but only for modules where you created such a custom field (because the file we modified is a common object for all modules in Dolibarr, not just invoices, thus this modification will gracefully skip this custom field if it does not exists for other modules).&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Add the generator&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/tpl/objectline_view.tpl.php and try to find the following block of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php if ($line-&amp;gt;special_code == 3)	{ ?&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo $langs-&amp;gt;trans(&#039;Option&#039;); ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } else { ?&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is what prints the total HT price value for each line.&lt;br /&gt;
&lt;br /&gt;
Just above of this block of code, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $object-&amp;gt;table_element_line; // you can use $this-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
if (isset($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
    &amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient; ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just like for the column title, this code will take care of generating the coefficient value only if the custom field is defined, because this file is also a generic template for all modules of Dolibarr.&lt;br /&gt;
&lt;br /&gt;
At this point, everything is done, you should see the following:&lt;br /&gt;
[[File:Cf_wiki_coeff_column.png]]&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in margin module ===&lt;br /&gt;
&lt;br /&gt;
If you enabled the margin module, you should see that the coefficient is not applied there, but it&#039;s easy to fix that.&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/class/commonobject.class.php and find the function getMarginInfos(), then inside find the following lines:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach($this-&amp;gt;lines as $line) {&lt;br /&gt;
    if (empty($line-&amp;gt;pa_ht) &amp;amp;&amp;amp; isset($line-&amp;gt;fk_fournprice) &amp;amp;&amp;amp; !$force_price) {&lt;br /&gt;
        $product = new ProductFournisseur($this-&amp;gt;db);&lt;br /&gt;
        if ($product-&amp;gt;fetch_product_fournisseur_price($line-&amp;gt;fk_fournprice))&lt;br /&gt;
            $line-&amp;gt;pa_ht = $product-&amp;gt;fourn_unitprice * (1 - $product-&amp;gt;fourn_remise_percent / 100);&lt;br /&gt;
        if (isset($conf-&amp;gt;global-&amp;gt;MARGIN_TYPE) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;MARGIN_TYPE == &amp;quot;2&amp;quot; &amp;amp;&amp;amp; $product-&amp;gt;fourn_unitcharges &amp;gt; 0)&lt;br /&gt;
            $line-&amp;gt;pa_ht += $product-&amp;gt;fourn_unitcharges;&lt;br /&gt;
    }&lt;br /&gt;
    // si prix d&#039;achat non renseigné et devrait l&#039;être, alors prix achat = prix vente&lt;br /&gt;
    if ((!isset($line-&amp;gt;pa_ht) || $line-&amp;gt;pa_ht == 0) &amp;amp;&amp;amp; $line-&amp;gt;subprice &amp;gt; 0 &amp;amp;&amp;amp; (isset($conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull == 1)) {&lt;br /&gt;
        $line-&amp;gt;pa_ht = $line-&amp;gt;subprice * (1 - ($line-&amp;gt;remise_percent / 100));&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the lines are filled with margin infos. The price has just been fetched here, and we will now modify it with the coefficient before the price gets used for margin computation.&lt;br /&gt;
&lt;br /&gt;
Just below the code we found, paste the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $line-&amp;gt;table_element;&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
if (!empty($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
    $line-&amp;gt;pa_ht = $line-&amp;gt;pa_ht * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient;&lt;br /&gt;
    $line-&amp;gt;subprice = $line-&amp;gt;subprice * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s all, the price should be reflected in the margin.&lt;br /&gt;
[[File:Cf_wiki_coeff_margin.png]]&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in remaining debt to pay ===&lt;br /&gt;
&lt;br /&gt;
Open /htdocs/core/class/commonobject.class.php and look for the function update_price().&lt;br /&gt;
&lt;br /&gt;
You should see the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function update_price($exclspec=0,$roundingadjust=&#039;none&#039;,$nodatabaseupdate=0,$seller=&#039;&#039;)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just below the include_once line, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;;&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/conf/conf_customfields_func.lib.php&#039;;&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $this-&amp;gt;table_element_line);&lt;br /&gt;
// Fetching line ids from parent object id&lt;br /&gt;
$line_ids = $customfields-&amp;gt;fetchAny(&#039;rowid&#039;, MAIN_DB_PREFIX.$this-&amp;gt;table_element_line, $this-&amp;gt;fk_element.&#039; = &#039;.$this-&amp;gt;id);&lt;br /&gt;
$line_ids = array_values_recursive(&#039;rowid&#039;, $line_ids);&lt;br /&gt;
// Load custom fields&lt;br /&gt;
$cf_records = $customfields-&amp;gt;fetch($line_ids);&lt;br /&gt;
// Reassociate the records with the line id (instead of the customfields record id)&lt;br /&gt;
$cf_line_cfrowid_to_rowid = array_reassociate(&#039;rowid&#039;, &#039;fk_&#039;.$this-&amp;gt;table_element_line, $cf_records);&lt;br /&gt;
$tmp = $cf_records;&lt;br /&gt;
$cf_records = array();&lt;br /&gt;
foreach ($tmp as $k=&amp;gt;$v) { $cf_records[$cf_line_cfrowid_to_rowid[$k]] = $tmp[$k]; }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will load the coefficient custom field (and also any other custom field you have defined for lines).&lt;br /&gt;
&lt;br /&gt;
Now scroll below, and you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
while ($i &amp;lt; $num)&lt;br /&gt;
{&lt;br /&gt;
    $obj = $this-&amp;gt;db-&amp;gt;fetch_object($resql);&lt;br /&gt;
&lt;br /&gt;
    // Note: There is no check on detail line and no check on total, if $forcedroundingmode = &#039;none&#039;&lt;br /&gt;
&lt;br /&gt;
    if ($forcedroundingmode == &#039;0&#039;)	// Check if data on line are consistent. This may solve lines that were not consistent because set with $forcedroundingmode=&#039;auto&#039;&lt;br /&gt;
    {&lt;br /&gt;
        $localtax_array=array($obj-&amp;gt;localtax1_type,$obj-&amp;gt;localtax1_tx,$obj-&amp;gt;localtax2_type,$obj-&amp;gt;localtax2_tx);&lt;br /&gt;
        $tmpcal=calcul_price_total($obj-&amp;gt;qty, $obj-&amp;gt;up, $obj-&amp;gt;remise_percent, $obj-&amp;gt;vatrate, $obj-&amp;gt;localtax1_tx, $obj-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $obj-&amp;gt;info_bits, $obj-&amp;gt;product_type, $seller, $localtax_array);&lt;br /&gt;
        $diff=price2num($tmpcal[1] - $obj-&amp;gt;total_tva, &#039;MT&#039;, 1);&lt;br /&gt;
        if ($diff)&lt;br /&gt;
        {&lt;br /&gt;
            $sqlfix=&amp;quot;UPDATE &amp;quot;.MAIN_DB_PREFIX.$this-&amp;gt;table_element_line.&amp;quot; SET &amp;quot;.$fieldtva.&amp;quot; = &amp;quot;.$tmpcal[1].&amp;quot;, total_ttc = &amp;quot;.$tmpcal[2].&amp;quot; WHERE rowid = &amp;quot;.$obj-&amp;gt;rowid;&lt;br /&gt;
            dol_syslog(&#039;We found unconsistent data into detailed line (difference of &#039;.$diff.&#039;) for line rowid = &#039;.$obj-&amp;gt;rowid.&amp;quot; (total vat of line calculated=&amp;quot;.$tmpcal[1].&amp;quot;, database=&amp;quot;.$obj-&amp;gt;total_tva.&amp;quot;). We fix the total_vat and total_ttc of line by running sqlfix = &amp;quot;.$sqlfix);&lt;br /&gt;
            $resqlfix=$this-&amp;gt;db-&amp;gt;query($sqlfix);&lt;br /&gt;
            if (! $resqlfix) dol_print_error($this-&amp;gt;db,&#039;Failed to update line&#039;);&lt;br /&gt;
            $obj-&amp;gt;total_tva = $tmpcal[1];&lt;br /&gt;
            $obj-&amp;gt;total_ttc = $tmpcal[2];&lt;br /&gt;
            //&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just after, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
$cf_coeff = $cf_records[$obj-&amp;gt;rowid]-&amp;gt;coefficient;&lt;br /&gt;
if (empty($cf_coeff) and $cf_coeff !== 0) $cf_coeff = 1; // by default, an empty coefficient = 1&lt;br /&gt;
$obj-&amp;gt;total_ht = $obj-&amp;gt;total_ht * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_tva = $obj-&amp;gt;total_tva * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_localtax1 = $obj-&amp;gt;total_localtax1 * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_localtax2 = $obj-&amp;gt;total_localtax2 * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_ttc = $obj-&amp;gt;total_ttc * $cf_coeff;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should do the trick. Now go to one of your invoices (or whatever module you are targetting), and try to add/edit a product/service line to force refresh the total price. You should then see this:&lt;br /&gt;
[[File:Cf_wiki_coeff_remaining_debt.png]]&lt;br /&gt;
&lt;br /&gt;
= Custom tax per product =&lt;br /&gt;
&lt;br /&gt;
This part will show you how to create a static custom tax per product. Static means that the tax will be stored per product, and not per product&#039;s line, thus you just have to setup the tax once per product and it will always be applied in every invoice you add this product.&lt;br /&gt;
&lt;br /&gt;
To do that, you will need the CustomFields Pro module + ProductsEasyAccess.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
1- Create a product&#039;s custom fields called &amp;quot;custom_tax&amp;quot;. Now, using ProductsEasyAccess, you can access this field wherever you wants, either to show it on the Dolibarr&#039;s interface (see previous chapter), or in a PDF template, which we will describe below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2- Modify your invoice&#039;s PDF template. You first need to load all the products&#039; fields &#039;&#039;before&#039;&#039; the loop over the products&#039; lines. To do this, search for a line like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// This should be placed above the &amp;quot;loop lines&amp;quot; below, add this:&lt;br /&gt;
&lt;br /&gt;
dol_include_once(&#039;/productseasyaccess/lib/productseasyaccess.lib.php&#039;);&lt;br /&gt;
fill_object_with_products_fields($object);&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines, search for this command&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  // Add this just below the for loop, this will ease your php manipulation after.&lt;br /&gt;
  // This will allow you to more easily access the products&#039; fields (this will store the id of each product inside $lineproductid).&lt;br /&gt;
  $line_product_id = $object-&amp;gt;lines[$i]-&amp;gt;fk_product;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4- You can now use your products&#039; fields in your PDF. To access the custom_tax field, just use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And to print it in your PDF:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell(0,3, $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax, 0, &#039;L&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5- Then, if you want to count the custom tax total and add it to your vat total, you can create a counter inside the for loop we cited in the first step:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_custom_tax = 0; // Initialize the custom tax total counter&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  $object-&amp;gt;total_custom_tax += $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then find inside the function **_tableau_tot**, find the following line:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And prepend the total modification like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc + $object-&amp;gt;total_custom_tax; // prepend this to add the custom tax to the total&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Done. This should show your custom tax per product and your total including all custom taxes.&lt;/div&gt;</summary>
		<author><name>Lrq3000</name></author>
	</entry>
	<entry>
		<id>https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33068</id>
		<title>Module CustomFields Cases</title>
		<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33068"/>
		<updated>2015-01-15T15:40:47Z</updated>

		<summary type="html">&lt;p&gt;Lrq3000: Added case &amp;quot;Modify the total global price with a coefficient (without tampering the discount)&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:CustomFields]]&lt;br /&gt;
{{TemplateDocDevEn}}&lt;br /&gt;
{{TemplateModEN}}&lt;br /&gt;
{{BasculeDevUserEn|&lt;br /&gt;
name=CustomFields|&lt;br /&gt;
num=8500|&lt;br /&gt;
devdoc=[[Module_CustomFields_Dev]]|&lt;br /&gt;
userdoc=[[Module_CustomFields]]|}}&lt;br /&gt;
This page lists a compilation of concrete, practical examples uses of [[Module_CustomFields|CustomFields]] and of its various features.&lt;br /&gt;
&lt;br /&gt;
If you had an experience with CustomFields, you can also add here your case as an example for others to get inspired.&lt;br /&gt;
&lt;br /&gt;
= How to make a generic PDF template listing all custom fields =&lt;br /&gt;
&lt;br /&gt;
To make a generic PDF template listing all custom fields, you just need to take an existing PDF template, then you need to follow these 3 steps:&lt;br /&gt;
&lt;br /&gt;
1/ Edit the class name (required, else it will conflict with the original template).&lt;br /&gt;
&lt;br /&gt;
Eg: if we take the crabe template for invoices, we change:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;crabe&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
into:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_customfields extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;customfields&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2/ Copy this generic function at the end of the PDF:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propale/order/etc... (CustomFields simpler functions will automatically adapt)&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs); // set default PDF font size&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
    // Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
    $customfields = customfields_fill_object($object, null, $outputlangs, null, true);&lt;br /&gt;
&lt;br /&gt;
    // Setting the starting position of the text cursor&lt;br /&gt;
    $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
    $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
    // Printing the customfields&lt;br /&gt;
    foreach ($object-&amp;gt;customfields as $label=&amp;gt;$value) { // $value is already formatted!&lt;br /&gt;
        // Get translated label&lt;br /&gt;
        $translatedlabel = $customfields-&amp;gt;findLabelPDF($label, $outputlangs); // translated label of the customfield (not translated by default in customfields_fill_object() because a field should always be accessible by a base name, whatever the translation is)&lt;br /&gt;
&lt;br /&gt;
        // PDF formatting, placement and printing&lt;br /&gt;
        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
        $pdf-&amp;gt;MultiCell(0,3, $translatedlabel.&#039; ($object-&amp;gt;customfields-&amp;gt;&#039;.$label.&#039;): &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternative function (with a bit less functionalities, but more controllable):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page (used to debug if CustomFields setup is correct)&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propal/product/whatever...&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs);&lt;br /&gt;
&lt;br /&gt;
            if (empty($object-&amp;gt;table_element) or empty($object-&amp;gt;id)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;Current \$object is not compatible with CustomFields, could not find table_element or id.&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
    $customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element);&lt;br /&gt;
&lt;br /&gt;
            // Fetching custom fields records&lt;br /&gt;
            $fields = $customfields-&amp;gt;fetch($object-&amp;gt;id);&lt;br /&gt;
&lt;br /&gt;
            if (!isset($fields)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;No custom field could be found for this object. Please check your configuration (did you create at least one customfield and set a value in the current datasheet?)&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            } else {&lt;br /&gt;
                // Setting the starting position of the text cursor&lt;br /&gt;
                $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
                $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
                // Printing the customfields&lt;br /&gt;
                foreach ($fields as $key=&amp;gt;$field) {&lt;br /&gt;
                        $translatedname = $customfields-&amp;gt;findLabelPDF($key, $outputlangs); // label of the customfield&lt;br /&gt;
                        $value = $customfields-&amp;gt;simpleprintFieldPDF($key, $field, $outputlangs); // value (cleaned and properly formatted) of the customfield&lt;br /&gt;
&lt;br /&gt;
                        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
                        $pdf-&amp;gt;MultiCell(0,3, $translatedname.&#039;: &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
                        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3/ Call the _pagecustomfields function at the right place (generally, after _pagefoot and just before $pdf-&amp;gt;Close()):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Pied de page&lt;br /&gt;
$this-&amp;gt;_pagefoot($pdf,$object,$outputlangs);&lt;br /&gt;
$pdf-&amp;gt;AliasNbPages();&lt;br /&gt;
&lt;br /&gt;
// CustomFields&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) { // if the customfields module is activated...&lt;br /&gt;
        // Add a page with the customfields&lt;br /&gt;
        $pdf-&amp;gt;AddPage();&lt;br /&gt;
        $this-&amp;gt;_pagecustomfields($pdf,$object,$outputlangs);&lt;br /&gt;
        $pagenb++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
$pdf-&amp;gt;Close(); // you should first search for this, you should only find one occurrence&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then go to the module&#039;s admin panel (eg: invoice admin panel) and enable the template you&#039;ve just created.&lt;br /&gt;
&lt;br /&gt;
You can now use it to test if your customfields work.&lt;br /&gt;
&lt;br /&gt;
= Conditions on fields selection =&lt;br /&gt;
&lt;br /&gt;
Sometimes you may want to show a DropdownBox or Constraint (or another multi-choice type), but you may want to show or hide choices based on conditions (eg: only if it&#039;s a prospect, only if this zone was selected, only if this category was selected, etc..).&lt;br /&gt;
&lt;br /&gt;
To know which is the best solution you should choose to implement your condition, you should first know in which category your condition falls in.&lt;br /&gt;
&lt;br /&gt;
== Conditions categories ==&lt;br /&gt;
&lt;br /&gt;
Conditions can be categorized into 4 types:&lt;br /&gt;
&lt;br /&gt;
* static condition: choices are restrained on a condition that never changes (eg: only show third-parties that are suppliers, or lowercase all data on sql insertion, or check that a field is above a certain number, etc.).&lt;br /&gt;
* semi-dynamic condition: choices that are constrained on a static table but based on the value of another field/customfield. This is also commonly called &amp;quot;Cascaded dropdown lists&amp;quot; or &amp;quot;Dynamically linked dropdown lists&amp;quot;.&lt;br /&gt;
* dynamic: everything is dynamic: choices are based on a subfield of a field of the current module, so that there&#039;s no material table and it&#039;s impossible to create a view (because you would need to create one view per id) (eg: show only the contacts attached to the third-party being accessed)&lt;br /&gt;
* timed condition: a condition that is based on time or recurrence (eg: at 5:00 AM delete old records, etc..)&lt;br /&gt;
&lt;br /&gt;
== Conditions solutions ==&lt;br /&gt;
&lt;br /&gt;
Now that you know what kind of condition you want to set, you can choose one of the following solutions depending on your preferences and knowledge.&lt;br /&gt;
&lt;br /&gt;
In any case, you can always use the &#039;&#039;&#039;CustomFields&#039;s overload functions&#039;&#039;&#039; to fit your needs for nearly every type of condition.&lt;br /&gt;
&lt;br /&gt;
=== Static condition ===&lt;br /&gt;
&lt;br /&gt;
* View: create a view on the table based on the condition, and then create a custom field constrained on this view (which is just like any table), eg: only show third-parties that are suppliers:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
CREATE VIEW llx_societe_supplier_view AS&lt;br /&gt;
SELECT *&lt;br /&gt;
FROM llx_societe&lt;br /&gt;
WHERE fournisseur=1;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
WARNING: foreign keys (and thus constraints) can&#039;t be used on views with MySQL, and materialized views neither exist, so you can&#039;t use this method with MySQL.&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
* SQL Transformations&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
=== Semi-dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade option is ideal in most cases, and if not enough, you can use the Cascade Custom option and Overloading Functions.&lt;br /&gt;
* View&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
&lt;br /&gt;
=== Dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade Custom and Overload functions&lt;br /&gt;
* Making your own module, which calls the CustomFields class&lt;br /&gt;
&lt;br /&gt;
=== Timed condition ===&lt;br /&gt;
&lt;br /&gt;
* SQL scheduled events&lt;br /&gt;
* Cron job&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
= Overloading functions =&lt;br /&gt;
&lt;br /&gt;
Here are a few concrete use cases of overloading functions to achieve various goals.&lt;br /&gt;
&lt;br /&gt;
== Dynamic list selection based on another one ==&lt;br /&gt;
&lt;br /&gt;
The goal is to show two custom fields on the Third-Party module: one which gives the zone (secteur) of the third-party (Isere, Alpes du sud, Haute-Savoie...) and the other which gives relative to the zone the list of all the ski resorts (station_a) inside the selected zone.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will show how to manually manage this kind of requirements using [[Module_CustomFields#Overloading_functions:_Adding_your_own_management_code_for_custom_fields|overloading functions]], but in latest CustomFields releases, the same solution can be achieved automatically using the [[Module_CustomFields#Cascade|Cascade option]].&lt;br /&gt;
&lt;br /&gt;
We have one table &#039;&#039;&#039;llx_k_station&#039;&#039;&#039; (rowid, station (char50), secteur(char50)) which contains the following: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1,les 2 alpes,isère&lt;br /&gt;
2,chamrousse,isère&lt;br /&gt;
3,alpe d&#039;huez,isère&lt;br /&gt;
4,vars,alpes du sud&lt;br /&gt;
5,risoul,alpes du sud&lt;br /&gt;
etc...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In CustomFields&#039;s admin panel, we create two custom fields:&lt;br /&gt;
&lt;br /&gt;
1- secteur: returns the list of all zones - type DropdownBox: enum(&#039;- Aucun&#039;,&#039;Alpes du Sud&#039;,&#039;export&#039;,&#039;Haute-Savoie&#039;,&#039;Isère&#039;, etc...)&lt;br /&gt;
&lt;br /&gt;
2- station_a: returns the list of all ski resorts - type constraint on &#039;&#039;&#039;llx_k_station&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Here is the code to put in customfields_fields_extend.lib.php that will allow to show only the ski resorts corresponding to the selected zone:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_editview_societe_station_a (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$parameters, &amp;amp;$action, &amp;amp;$user, &amp;amp;$idvar, &amp;amp;$rightok, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
  global $db; // allow to access database functions&lt;br /&gt;
  $sql=&amp;quot;SELECT llx_k_station.station, llx_k_station.rowid FROM llx_societe_customfields INNER JOIN llx_k_station ON llx_societe_customfields.Secteur = llx_k_station.secteur WHERE llx_societe_customfields.fk_societe=&amp;quot;.$object-&amp;gt;id;&lt;br /&gt;
$result=$db-&amp;gt;query($sql);&lt;br /&gt;
 &lt;br /&gt;
  if ($result) {&lt;br /&gt;
    $num = $db-&amp;gt;num_rows($result);&lt;br /&gt;
    $i = 0;&lt;br /&gt;
    $myarray = array();&lt;br /&gt;
    while ($i &amp;lt; $num)&lt;br /&gt;
    {&lt;br /&gt;
      $obj = $db-&amp;gt;fetch_object($result);&lt;br /&gt;
      $myarray []=array(&#039;id&#039;=&amp;gt;$obj-&amp;gt;rowid, &#039;value&#039;=&amp;gt;$obj-&amp;gt;station);&lt;br /&gt;
      $i++;&lt;br /&gt;
    }&lt;br /&gt;
    $value = $myarray; &lt;br /&gt;
    $db-&amp;gt;free($result);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to manub for giving this case.&lt;br /&gt;
&lt;br /&gt;
== Dynamic discount computation per products lines on propales ==&lt;br /&gt;
&lt;br /&gt;
The goal here is to compute the discount for a product line in propales based on a computation on three custom fields.&lt;br /&gt;
&lt;br /&gt;
First, create three custom fields for &amp;quot;Line commercial proposals&amp;quot;: vl, cpl and lc.&lt;br /&gt;
&lt;br /&gt;
Then add the following overloading function at the end of your customfields_fields_extend.lib.php file (read the comments for more informations):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_propaldet_vl (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
&lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $vl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;vl&#039;};&lt;br /&gt;
    $cpl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;cpl&#039;};&lt;br /&gt;
    $lc = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;lc&#039;};&lt;br /&gt;
&lt;br /&gt;
    // Compute the discount&lt;br /&gt;
    $remise_calculee = (100 - $vl) * (100 - $cpl) * (100 - $lc);&lt;br /&gt;
&lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $remise_calculee = price2num($remise_calculee);&lt;br /&gt;
&lt;br /&gt;
    // Assign the new discount into the PropalLine object&lt;br /&gt;
    $object-&amp;gt;remise_percent = $remise_calculee;&lt;br /&gt;
&lt;br /&gt;
    // Update the price (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
&lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
&lt;br /&gt;
    //$q = &amp;quot;UPDATE  &amp;quot;.MAIN_DB_PREFIX.&amp;quot;propaldet SET remise_percent=&amp;quot;.$remise_calculee.&amp;quot; WHERE  rowid=&amp;quot;.$object-&amp;gt;rowid;&lt;br /&gt;
    //$db-&amp;gt;query($q);&lt;br /&gt;
    &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;vl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;cpl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;lc&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Linking Dolibarr objects from two different modules =&lt;br /&gt;
&lt;br /&gt;
Dolibarr sometimes offers the possibility to link two different modules. Eg: when you convert an Intervention card into an Invoice.&lt;br /&gt;
&lt;br /&gt;
In this case, you might want in your templates to fetch the custom fields of the linked object. Eg: you generate a PDF from the Invoice, but you want to get the custom fields from the Intervention that was converted into the Invoice.&lt;br /&gt;
&lt;br /&gt;
The key here is to use the &#039;&#039;&#039;llx_element_element&#039;&#039;&#039; table or the &#039;&#039;&#039;llx_element_*&#039;&#039;&#039; tables (eg: llx_element_contact): this is a standard Dolibarr table that stores all links between objects of two different modules.&lt;br /&gt;
&lt;br /&gt;
Here is the structure:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
rowid | fk_source | sourcetype | fk_target | targettype&lt;br /&gt;
-------------------------------------------------------&lt;br /&gt;
1 | 2 | fichinter | 5 | facture&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can then use a php code like the following to populate your $object with customfields:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
// This will also create and returns us an instance of the CustomFields class, which we will use to manually query the llx_element_element table&lt;br /&gt;
$customfields = customfields_fill_object($object, null, $outputlangs, &#039;facture&#039;, true); // beautified values&lt;br /&gt;
&lt;br /&gt;
// Querying the llx_element_element table manually to get the link between the invoice and the linked intervention card (we want the id of the intervention card)&lt;br /&gt;
// fetchAny($columns, $table, $where=&#039;&#039;, $orderby=&#039;&#039;, $limitby=&#039;&#039;)&lt;br /&gt;
$eltrow = $customfields-&amp;gt;fetchAny(&#039;*&#039;, MAIN_DB_PREFIX.&#039;element_element&#039;, &#039;targettype=&amp;quot;&#039;.$object-&amp;gt;element.&#039;&amp;quot; and fk_target=&#039;.$object-&amp;gt;id.&#039; and sourcetype=&amp;quot;fichinter&amp;quot;&#039;);&lt;br /&gt;
&lt;br /&gt;
// Save the intervention&#039;s id&lt;br /&gt;
$sourceid = $eltrow[0][&#039;fk_source&#039;];&lt;br /&gt;
&lt;br /&gt;
// Crafting a dummy intervention object&lt;br /&gt;
$fromobject = new stdClass(); // Init an empty object&lt;br /&gt;
$fromobject-&amp;gt;id = $sourceid; // We need the id and..&lt;br /&gt;
$fromobject-&amp;gt;table_element = &#039;fichinter&#039;; // the table_element (name of the table)&lt;br /&gt;
&lt;br /&gt;
// We can then fill $object with the intervention custom fields (we store them in a sub property &#039;fichinter&#039; to avoid conflicts with invoice&#039;s custom fields)&lt;br /&gt;
customfields_fill_object($object, $fromobject, $outputlangs, &#039;fichinter&#039;, true);&lt;br /&gt;
&lt;br /&gt;
// Now you can access invoices custom fields using:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;facture-&amp;gt;cf_myfield);&lt;br /&gt;
// And access intervention custom fields with:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;fichinter-&amp;gt;cf_myfield);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to netassopro for the tip.&lt;br /&gt;
&lt;br /&gt;
= Modify the total global price with a coefficient (without tampering the discount) =&lt;br /&gt;
&lt;br /&gt;
This is more troublesome than modifying the total price per product/service line, because the total global price of an object (let&#039;s say an invoice, but this also works for any other object) is always recomputed by summing the prices and quantity of each product/service line. This means that although there&#039;s an entry &amp;quot;total_ttc&amp;quot; in the database to store the invoice&#039;s total price, this price is in fact always recomputed, it&#039;s never used as-is. Thus, it&#039;s useless to change only the database record: we need to inject some code to change how the total price is computed.&lt;br /&gt;
&lt;br /&gt;
This could be done using an overloading function or a trigger, but unluckily, Dolibarr doesn&#039;t call a trigger nor a hook when recomputing the price. We thus need to edit core files to do what we want.&lt;br /&gt;
&lt;br /&gt;
However, the bright side is that we can cleverly edit just two functions from one core file: update_price() and getMarginInfos() from /htdocs/core/class/commonobject.class.php , and everything should run smoothly. Beware that if you update Dolibarr, you will need to redo these two modifications in the code.&lt;br /&gt;
&lt;br /&gt;
First, you need to create a new custom field called &amp;quot;global_coefficient&amp;quot; in Invoices (not Invoices Lines!). Then you can continue to the subchapters below.&lt;br /&gt;
&lt;br /&gt;
Also note that this global coefficient is also compatible with per line coefficients (as described in the next chapter), thus you can use a global coefficient and per line coefficients at the same time. You can also use multiple global coefficient if you wish.&lt;br /&gt;
&lt;br /&gt;
Another note: if you have a grid of discounts depending on quantity or price or whatever parameter, you can program a precomputation code using a Custom Ajax function (this way, your discounts will be automatically computed, but then you can still manually enter them for particular cases since the custom field is editable).&lt;br /&gt;
&lt;br /&gt;
== Implement the change of global price in most of Dolibarr (including remaining debt to pay and PDF and ODT) ==&lt;br /&gt;
&lt;br /&gt;
The change we will implement here will reflect the new total price with the global coefficient in most of Dolibarr&#039;s code, since we will here change the main function that is usually called to update and get the total price.&lt;br /&gt;
&lt;br /&gt;
Open /htdocs/core/class/commonobject.class.php and look for the function update_price().&lt;br /&gt;
&lt;br /&gt;
You should see the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function update_price($exclspec=0,$roundingadjust=&#039;none&#039;,$nodatabaseupdate=0,$seller=&#039;&#039;)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just below the include_once line, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;;&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/conf/conf_customfields_func.lib.php&#039;;&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $this-&amp;gt;table_element);&lt;br /&gt;
// Get object&#039;s id (either stored in property &amp;quot;id&amp;quot; or &amp;quot;rowid&amp;quot;)&lt;br /&gt;
$this_id = (!empty($this-&amp;gt;id)) ? $this-&amp;gt;id : $this-&amp;gt;rowid;&lt;br /&gt;
// Load custom fields records&lt;br /&gt;
$cf_parent_record = $customfields-&amp;gt;fetch($this_id);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will load the global_coefficient custom field (and also any other custom field you have defined for invoices).&lt;br /&gt;
&lt;br /&gt;
Now scroll below, and you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
while ($i &amp;lt; $num)&lt;br /&gt;
{&lt;br /&gt;
    $obj = $this-&amp;gt;db-&amp;gt;fetch_object($resql);&lt;br /&gt;
&lt;br /&gt;
    ... (lots of code)&lt;br /&gt;
&lt;br /&gt;
    i++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Add revenue stamp to total&lt;br /&gt;
$this-&amp;gt;total_ttc       += isset($this-&amp;gt;revenuestamp)?$this-&amp;gt;revenuestamp:0;&lt;br /&gt;
&lt;br /&gt;
$this-&amp;gt;db-&amp;gt;free($resql);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is the end of the loop that is summing each product/service line price to compute the total price. Just after this loop, you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Now update global field total_ht, total_ttc and tva&lt;br /&gt;
$fieldht=&#039;total_ht&#039;;&lt;br /&gt;
$fieldtva=&#039;tva&#039;;&lt;br /&gt;
$fieldlocaltax1=&#039;localtax1&#039;;&lt;br /&gt;
$fieldlocaltax2=&#039;localtax2&#039;;&lt;br /&gt;
$fieldttc=&#039;total_ttc&#039;;&lt;br /&gt;
// Specific code for backward compatibility with old field names&lt;br /&gt;
if ($this-&amp;gt;element == &#039;facture&#039; || $this-&amp;gt;element == &#039;facturerec&#039;)             $fieldht=&#039;total&#039;;&lt;br /&gt;
if ($this-&amp;gt;element == &#039;facture_fourn&#039; || $this-&amp;gt;element == &#039;invoice_supplier&#039;) $fieldtva=&#039;total_tva&#039;;&lt;br /&gt;
if ($this-&amp;gt;element == &#039;propal&#039;)                                                $fieldttc=&#039;total&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Between those two blocks of code (so just above // Now update global field total_ht, total_ttc and tva), you can place the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
if (!empty($cf_parent_record-&amp;gt;global_coefficient)) { // avoid applying the coefficient if empty (this will return a 0 price)&lt;br /&gt;
    $this-&amp;gt;total_ht = $this-&amp;gt;total_ht * $cf_parent_record-&amp;gt;global_coefficient;&lt;br /&gt;
    $this-&amp;gt;total_tva = $this-&amp;gt;total_tva * $cf_parent_record-&amp;gt;global_coefficient;&lt;br /&gt;
    $this-&amp;gt;total_ttc = $this-&amp;gt;total_ttc * $cf_parent_record-&amp;gt;global_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Done, now try to add/edit a product/service line, and the total price should be affected by the global coefficient.&lt;br /&gt;
&lt;br /&gt;
== Implementing the change of price in margin module ==&lt;br /&gt;
&lt;br /&gt;
If you enabled the margin module, you should see that the coefficient is not applied there, but it&#039;s easy to fix that.&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/class/commonobject.class.php and find the function getMarginInfos(), then inside find the following lines:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach($this-&amp;gt;lines as $line) {&lt;br /&gt;
    if (empty($line-&amp;gt;pa_ht) &amp;amp;&amp;amp; isset($line-&amp;gt;fk_fournprice) &amp;amp;&amp;amp; !$force_price) {&lt;br /&gt;
        $product = new ProductFournisseur($this-&amp;gt;db);&lt;br /&gt;
        if ($product-&amp;gt;fetch_product_fournisseur_price($line-&amp;gt;fk_fournprice))&lt;br /&gt;
            $line-&amp;gt;pa_ht = $product-&amp;gt;fourn_unitprice * (1 - $product-&amp;gt;fourn_remise_percent / 100);&lt;br /&gt;
        if (isset($conf-&amp;gt;global-&amp;gt;MARGIN_TYPE) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;MARGIN_TYPE == &amp;quot;2&amp;quot; &amp;amp;&amp;amp; $product-&amp;gt;fourn_unitcharges &amp;gt; 0)&lt;br /&gt;
            $line-&amp;gt;pa_ht += $product-&amp;gt;fourn_unitcharges;&lt;br /&gt;
    }&lt;br /&gt;
    // si prix d&#039;achat non renseigné et devrait l&#039;être, alors prix achat = prix vente&lt;br /&gt;
    if ((!isset($line-&amp;gt;pa_ht) || $line-&amp;gt;pa_ht == 0) &amp;amp;&amp;amp; $line-&amp;gt;subprice &amp;gt; 0 &amp;amp;&amp;amp; (isset($conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull == 1)) {&lt;br /&gt;
        $line-&amp;gt;pa_ht = $line-&amp;gt;subprice * (1 - ($line-&amp;gt;remise_percent / 100));&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the lines are filled with margin infos. The price has just been fetched here, and we will now modify it with the coefficient before the price gets used for margin computation.&lt;br /&gt;
&lt;br /&gt;
Just below the code we found, paste the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Global coefficient (CustomFields)&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
// Get object&#039;s id (either stored in property &amp;quot;id&amp;quot; or &amp;quot;rowid&amp;quot;)&lt;br /&gt;
$this_id = (!empty($this-&amp;gt;id)) ? $this-&amp;gt;id : $this-&amp;gt;rowid;&lt;br /&gt;
// Load custom fields records&lt;br /&gt;
$customfields = customfields_fill_object($this);&lt;br /&gt;
// Apply the coefficient&lt;br /&gt;
if (!empty($this-&amp;gt;customfields-&amp;gt;cf_global_coefficient)) {&lt;br /&gt;
    $line-&amp;gt;pa_ht = $line-&amp;gt;pa_ht * $this-&amp;gt;customfields-&amp;gt;cf_global_coefficient;&lt;br /&gt;
    $line-&amp;gt;subprice = $line-&amp;gt;subprice * $this-&amp;gt;customfields-&amp;gt;cf_global_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Note that here we cheated a bit: we do not compute the coefficient on the global total price, but rather per product/service line price. This is done on purpose, because mathematically this should produce no difference, and it also allows Dolibarr to continue peacefully with its margin calculations (else we would have to adapt all those computations, which would imply a lot more of code changes!).&lt;br /&gt;
&lt;br /&gt;
That&#039;s all, the price should be reflected in the margin. Just refresh the page to reflect the change.&lt;br /&gt;
&lt;br /&gt;
= Modify the total price PER product/service line with a coefficient (without tampering the discount) =&lt;br /&gt;
&lt;br /&gt;
Here we will go through the creation and management of a custom field called &#039;&#039;&#039;coefficient&#039;&#039;&#039; that will change the total price of each product by modifying it by the coefficient.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will not create a coefficient per product but rather per product&#039;s line. Thus if you want to create a coefficient that never change between invoices but only depending on the product and stays static, please refer to the next chapter below about creating a custom tax per product.&lt;br /&gt;
&lt;br /&gt;
The preliminary and necessary step is to first create a custom field that will be used to modify the total price. We will create one for Client Invoice Lines.&lt;br /&gt;
&lt;br /&gt;
Go to the CustomFields administration panel, then into the Client Invoice Lines tab, then click on the button New Field, name the field &#039;&#039;&#039;coefficient&#039;&#039;&#039;, and set the type to &#039;&#039;&#039;Double&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
When you go to client invoices in Dolibarr, you should then see something similar to this:&lt;br /&gt;
[[File:Cfe1.jpg]]&lt;br /&gt;
You can see that our coefficient field was successfully created.&lt;br /&gt;
&lt;br /&gt;
Then here are two alternative ways of implementing the change of the total price of each product relativery to this coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change total price in database ==&lt;br /&gt;
&lt;br /&gt;
Here we will use a &amp;quot;save&amp;quot; overloading function to change the total price directly in the database, thus we won&#039;t have to modify anything else (eg: nor PDF templating nor ODT) since the total price will be already updated in the database.&lt;br /&gt;
&lt;br /&gt;
Warning: this method will work only with CustomFields =&amp;gt; 3.2.23 since the &amp;quot;save&amp;quot; overloading function did not work for products lines before.&lt;br /&gt;
&lt;br /&gt;
Just add the following overloading function in your customfields_fields_extend.lib.php file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_facturedet_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
 &lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $coefficient = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;coefficient&#039;}; // you could also use $coefficient = $value;&lt;br /&gt;
 &lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $coefficient = price2num($coefficient);&lt;br /&gt;
 &lt;br /&gt;
    // Get the original subprices (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
 &lt;br /&gt;
    // Update the subprices with the coefficient&lt;br /&gt;
    $object-&amp;gt;total_ht = $object-&amp;gt;total_ht * $coefficient; // compute no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva = $object-&amp;gt;total_tva * $coefficient; // compute tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc * $coefficient; // compute tax included price&lt;br /&gt;
 &lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(&#039;&#039;,true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
 &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;coefficient&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should show the correct total price on the Dolibarr&#039;s interface without modifying the unit subprice (P.U. Unit):&lt;br /&gt;
&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
You have nothing to edit in your PDF or ODT template, since the new subprice with applied coefficient will be automatically printed instead of the old one without coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change printed total price per product without tampering the subprice in the database ==&lt;br /&gt;
&lt;br /&gt;
Here is a way to change the printed total price (the one shown on screen and in PDF/ODT) but without changing the total price in the database.&lt;br /&gt;
&lt;br /&gt;
The following sub-chapters are all independent, thus you can only follow the ones that you require, eg: if you only need to change the price in your PDF documents, just create a custom field then jump to the sub-chapter to &#039;&#039;&#039;Implement the change of price in PDF&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in ODT templates ===&lt;br /&gt;
&lt;br /&gt;
There&#039;s no eval implementation currently inside ODT templates, but there is a kind of hook implementation, called [[Create_an_ODT_document_template#Other_personalized_tags|substitution functions]].&lt;br /&gt;
&lt;br /&gt;
You can either create your own module, then create your own substitution function, but for the sake of simplicity in this tutorial, we will directly use the CustomFields substitution function. However, please remember that you will have to redo your changes everytime you will update the CustomFields module, so if you don&#039;t want to do that it would be better if you create your own module and substitution function.&lt;br /&gt;
&lt;br /&gt;
Open the file &#039;&#039;&#039;htdocs/customfields/core/substitutions/functions_customfields.lib.php&#039;&#039;&#039; and go to the function &#039;&#039;&#039;customfields_completesubstitutionarray_lines&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
At the very end of this function, but inside the customfields check:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) {&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
But just before the ending bracket, you should add the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($object-&amp;gt;table_element_line === &#039;facturedet&#039;) { // First check that we are in the right module, we don&#039;t want to use coefficient for other modules if we didn&#039;t define the custom field there&lt;br /&gt;
    $substitutionarray[&#039;line_price_full&#039;] = $substitutionarray[&#039;line_price_ttc&#039;] * $substitutionarray[&#039;cf_coefficient&#039;]; // multiply the total price with our coefficient&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code will add a new tag &#039;&#039;&#039;{line_price_full}&#039;&#039;&#039; that you can then use inside your ODT templates.&lt;br /&gt;
&lt;br /&gt;
Just place this tag in a table in your ODT document, between the table tags ( [!-- BEGIN row.lines --] ... [!-- END row.lines --] ), and this will print your full price.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in PDF templates ===&lt;br /&gt;
&lt;br /&gt;
First, we will create our own PDF template by copying an existing template, and then we will edit it to change the price of items.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Creating your own PDF template by copying an existing template&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Go into the following folder: &#039;&#039;&#039;htdocs/core/modules/facture/doc/&#039;&#039;&#039;, then copy the file &#039;&#039;&#039;pdf_crabe.modules.php&#039;&#039;&#039; and name the new one &#039;&#039;&#039;pdf_crabecoeff.modules.php&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Then open it in your favourite editor, and change the following lines:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures // change pdf_crabe into pdf_crabecoeff&lt;br /&gt;
{&lt;br /&gt;
...&lt;br /&gt;
    function __construct($db)&lt;br /&gt;
    {&lt;br /&gt;
        ...&lt;br /&gt;
	$this-&amp;gt;name = &amp;quot;crabe&amp;quot;; // change &amp;quot;crabe&amp;quot; into &amp;quot;crabecoeff&amp;quot;&lt;br /&gt;
        ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you have your own PDF template!&lt;br /&gt;
&lt;br /&gt;
Now you should &#039;&#039;&#039;enable your PDF template&#039;&#039;&#039; before doing anything else, by going into the Modules Administration panel, and into the Invoice admin page, then click on the ON button at the right of the &#039;&#039;&#039;crabecoeff&#039;&#039;&#039; entry to enable the template.&lt;br /&gt;
&lt;br /&gt;
Now, let&#039;s proceed onto modifying the prices.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Changing the prices automatically in your PDF&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
You can now do pretty much anything you want in your PDF template, but I will give you a mean to automatically change the prices of every products with our coefficient, as well as the total price, without editing too much the template.&lt;br /&gt;
&lt;br /&gt;
First, try to find the following code of line (should be inside the write_file() method):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf=pdf_getInstance($this-&amp;gt;format);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, just copy/paste the following code &#039;&#039;&#039;below&#039;&#039;&#039; pdf_getInstance():&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$this-&amp;gt;customfields = customfields_fill_object($object, null, $outputlangs, null, true); // beautified values&lt;br /&gt;
$this-&amp;gt;customfields_raw = customfields_fill_object($object, null, $outputlangs, &#039;raw&#039;, null); // raw values&lt;br /&gt;
$this-&amp;gt;customfields_lines = customfields_fill_object_lines($object, null, $outputlangs, null, true); // product lines&#039; values&lt;br /&gt;
&lt;br /&gt;
// Resetting every $object price because we will compute them anew&lt;br /&gt;
$object-&amp;gt;total_ht = 0;&lt;br /&gt;
$object-&amp;gt;total_tva = 0;&lt;br /&gt;
$object-&amp;gt;total_ttc = 0;&lt;br /&gt;
// For each line of product/service, we will recompute all the prices&lt;br /&gt;
foreach($object-&amp;gt;lines as $line) {&lt;br /&gt;
    // Get the coefficient custom field&#039;s value for the current line&lt;br /&gt;
    $coeff = $object-&amp;gt;customfields-&amp;gt;lines-&amp;gt;{$line-&amp;gt;rowid}-&amp;gt;cf_coefficient;&lt;br /&gt;
    // Recompute the price of this product/service line&lt;br /&gt;
    $line-&amp;gt;total_ht = $line-&amp;gt;total_ht*$coeff; // compute no tax price&lt;br /&gt;
    $line-&amp;gt;total_tva = $line-&amp;gt;total_tva*$coeff; // compute tax amount&lt;br /&gt;
    $line-&amp;gt;total_ttc = $line-&amp;gt;total_ttc*$coeff; // compute tax included price&lt;br /&gt;
    // Do the sum of all new products/services prices and tva (will be incremented with every product/service line)&lt;br /&gt;
    $object-&amp;gt;total_ht += $line-&amp;gt;total_ht; // global no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva += $line-&amp;gt;total_tva; // global vat/tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc += $line-&amp;gt;total_ttc; // global  tax included price&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you should try to generate your PDF document, and you should get something like this:&lt;br /&gt;
[[File:Cfe3.jpg]]&lt;br /&gt;
&lt;br /&gt;
Note: printing the coefficient as a column in your PDF document is also possible but will not be covered here because PDF templates will vary for every persons depending on your needs.&lt;br /&gt;
For guidelines about how to do this, you can read the documentation on [[Create_a_PDF_document_template|how to create a PDF template]] and [[Module_CustomFields#Implementing_in_PDF_templates|how to use custom fields in PDF templates]].&lt;br /&gt;
&lt;br /&gt;
Hint: you should be looking inside the following conditional block, inside the write_file() method:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
{&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of total price in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
Unluckily there is not (yet) any hooking context to modify how lines of products/services are printed, thus the only thing you can do is directly edit the &#039;&#039;&#039;template file&#039;&#039;&#039; and manually put inside your code to load your custom field and print it the way you want using HTML formatting, for our example:&lt;br /&gt;
&lt;br /&gt;
* For Dolibarr 3.3 and above, edit the file &#039;&#039;&#039;htdocs/core/tpl/objectline_view.tpl.php&#039;&#039;&#039;&lt;br /&gt;
* For Dolibarr 3.1 and 3.2, edit the file &#039;&#039;&#039;htdocs/core/tpl/freeproductline_view.tpl.php&#039;&#039;&#039; AND &#039;&#039;&#039;predefinedproductline_view.tpl.php&#039;&#039;&#039; (apply the same changes to both files)&lt;br /&gt;
&lt;br /&gt;
In the template file you opened, find the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And replace it by:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $object-&amp;gt;table_element_line; // you can use $object-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
&lt;br /&gt;
// Modifying the HT price and printing it on the Dolibarr&#039;s datasheet interface&lt;br /&gt;
// Applying a multiplying/dividing coefficient to an HT price will give a mathematically correct TTC price since the taxes are also a multiplier&lt;br /&gt;
// However, if you want to apply other mathematical operations (like addition or substaction), you should use the total price with $line-&amp;gt;total_ttc&lt;br /&gt;
if (!empty($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
    echo price($line-&amp;gt;total_ht * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient);&lt;br /&gt;
} else {&lt;br /&gt;
    echo price($line-&amp;gt;total_ht);&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should produce something like this:&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Technical note: here we are using customfields_fill_object() instead of customfields_fill_object_lines() (the latter being specifically adapted to lines and thus should theoretically be used here) for performance reasons because the template file we are editing is called in a loop, and customfields_fill_object_lines() should be called outside a loop (because it already does the job that is done by the loop), so here we &#039;tricks&#039; the customfields_fill_object() function to avoid this loop by submitting &#039;facturedet&#039; as a module by itself (when in fact it&#039;s a product lines table, but it works the same way nevertheless). This is a good demonstration of the flexibility of the CustomFields functions and methods.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the coefficient column in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
It&#039;s also possible to show the coefficient in a new column on the Dolibarr interface.&lt;br /&gt;
&lt;br /&gt;
To do that, you need to do two things:&lt;br /&gt;
&lt;br /&gt;
1- Add the column title &amp;quot;Coeff.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
2- Add the generator that will input the coefficient value for each line.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Add the column title &amp;quot;Coeff.&amp;quot;&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
Open in your favourite editor the file /htdocs/core/class/commonobject.class.php and inside the function printObjectLines (note the plural s), find the following lines of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Total HT&lt;br /&gt;
print &#039;&amp;lt;td align=&amp;quot;right&amp;quot; width=&amp;quot;50&amp;quot;&amp;gt;&#039;.$langs-&amp;gt;trans(&#039;TotalHTShort&#039;).&#039;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is what prints the Total HT price column. We will place the Coeff column just before.&lt;br /&gt;
&lt;br /&gt;
So, just above this code, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
dol_include_once(&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element_line);&lt;br /&gt;
$coeff_field = $customfields-&amp;gt;fetchFieldStruct(&#039;coefficient&#039;);&lt;br /&gt;
if (!empty($coeff_field)) {&lt;br /&gt;
    print &#039;&amp;lt;td align=&amp;quot;right&amp;quot; width=&amp;quot;50&amp;quot;&amp;gt;&#039;.$langs-&amp;gt;trans(&#039;Coeff.&#039;).&#039;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will take care of showing the Coeff. column, but only for modules where you created such a custom field (because the file we modified is a common object for all modules in Dolibarr, not just invoices, thus this modification will gracefully skip this custom field if it does not exists for other modules).&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Add the generator&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/tpl/objectline_view.tpl.php and try to find the following block of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php if ($line-&amp;gt;special_code == 3)	{ ?&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo $langs-&amp;gt;trans(&#039;Option&#039;); ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } else { ?&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is what prints the total HT price value for each line.&lt;br /&gt;
&lt;br /&gt;
Just above of this block of code, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $object-&amp;gt;table_element_line; // you can use $this-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
if (isset($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
    &amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient; ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just like for the column title, this code will take care of generating the coefficient value only if the custom field is defined, because this file is also a generic template for all modules of Dolibarr.&lt;br /&gt;
&lt;br /&gt;
At this point, everything is done, you should see the following:&lt;br /&gt;
[[File:Cf_wiki_coeff_column.png]]&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in margin module ===&lt;br /&gt;
&lt;br /&gt;
If you enabled the margin module, you should see that the coefficient is not applied there, but it&#039;s easy to fix that.&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/class/commonobject.class.php and find the function getMarginInfos(), then inside find the following lines:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach($this-&amp;gt;lines as $line) {&lt;br /&gt;
    if (empty($line-&amp;gt;pa_ht) &amp;amp;&amp;amp; isset($line-&amp;gt;fk_fournprice) &amp;amp;&amp;amp; !$force_price) {&lt;br /&gt;
        $product = new ProductFournisseur($this-&amp;gt;db);&lt;br /&gt;
        if ($product-&amp;gt;fetch_product_fournisseur_price($line-&amp;gt;fk_fournprice))&lt;br /&gt;
            $line-&amp;gt;pa_ht = $product-&amp;gt;fourn_unitprice * (1 - $product-&amp;gt;fourn_remise_percent / 100);&lt;br /&gt;
        if (isset($conf-&amp;gt;global-&amp;gt;MARGIN_TYPE) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;MARGIN_TYPE == &amp;quot;2&amp;quot; &amp;amp;&amp;amp; $product-&amp;gt;fourn_unitcharges &amp;gt; 0)&lt;br /&gt;
            $line-&amp;gt;pa_ht += $product-&amp;gt;fourn_unitcharges;&lt;br /&gt;
    }&lt;br /&gt;
    // si prix d&#039;achat non renseigné et devrait l&#039;être, alors prix achat = prix vente&lt;br /&gt;
    if ((!isset($line-&amp;gt;pa_ht) || $line-&amp;gt;pa_ht == 0) &amp;amp;&amp;amp; $line-&amp;gt;subprice &amp;gt; 0 &amp;amp;&amp;amp; (isset($conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull == 1)) {&lt;br /&gt;
        $line-&amp;gt;pa_ht = $line-&amp;gt;subprice * (1 - ($line-&amp;gt;remise_percent / 100));&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the lines are filled with margin infos. The price has just been fetched here, and we will now modify it with the coefficient before the price gets used for margin computation.&lt;br /&gt;
&lt;br /&gt;
Just below the code we found, paste the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $line-&amp;gt;table_element;&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
if (!empty($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
    $line-&amp;gt;pa_ht = $line-&amp;gt;pa_ht * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient;&lt;br /&gt;
    $line-&amp;gt;subprice = $line-&amp;gt;subprice * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s all, the price should be reflected in the margin.&lt;br /&gt;
[[File:Cf_wiki_coeff_margin.png]]&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in remaining debt to pay ===&lt;br /&gt;
&lt;br /&gt;
Open /htdocs/core/class/commonobject.class.php and look for the function update_price().&lt;br /&gt;
&lt;br /&gt;
You should see the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function update_price($exclspec=0,$roundingadjust=&#039;none&#039;,$nodatabaseupdate=0,$seller=&#039;&#039;)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just below the include_once line, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;;&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/conf/conf_customfields_func.lib.php&#039;;&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $this-&amp;gt;table_element_line);&lt;br /&gt;
// Fetching line ids from parent object id&lt;br /&gt;
$line_ids = $customfields-&amp;gt;fetchAny(&#039;rowid&#039;, MAIN_DB_PREFIX.$this-&amp;gt;table_element_line, $this-&amp;gt;fk_element.&#039; = &#039;.$this-&amp;gt;id);&lt;br /&gt;
$line_ids = array_values_recursive(&#039;rowid&#039;, $line_ids);&lt;br /&gt;
// Load custom fields&lt;br /&gt;
$cf_records = $customfields-&amp;gt;fetch($line_ids);&lt;br /&gt;
// Reassociate the records with the line id (instead of the customfields record id)&lt;br /&gt;
$cf_line_cfrowid_to_rowid = array_reassociate(&#039;rowid&#039;, &#039;fk_&#039;.$this-&amp;gt;table_element_line, $cf_records);&lt;br /&gt;
$tmp = $cf_records;&lt;br /&gt;
$cf_records = array();&lt;br /&gt;
foreach ($tmp as $k=&amp;gt;$v) { $cf_records[$cf_line_cfrowid_to_rowid[$k]] = $tmp[$k]; }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will load the coefficient custom field (and also any other custom field you have defined for lines).&lt;br /&gt;
&lt;br /&gt;
Now scroll below, and you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
while ($i &amp;lt; $num)&lt;br /&gt;
{&lt;br /&gt;
    $obj = $this-&amp;gt;db-&amp;gt;fetch_object($resql);&lt;br /&gt;
&lt;br /&gt;
    // Note: There is no check on detail line and no check on total, if $forcedroundingmode = &#039;none&#039;&lt;br /&gt;
&lt;br /&gt;
    if ($forcedroundingmode == &#039;0&#039;)	// Check if data on line are consistent. This may solve lines that were not consistent because set with $forcedroundingmode=&#039;auto&#039;&lt;br /&gt;
    {&lt;br /&gt;
        $localtax_array=array($obj-&amp;gt;localtax1_type,$obj-&amp;gt;localtax1_tx,$obj-&amp;gt;localtax2_type,$obj-&amp;gt;localtax2_tx);&lt;br /&gt;
        $tmpcal=calcul_price_total($obj-&amp;gt;qty, $obj-&amp;gt;up, $obj-&amp;gt;remise_percent, $obj-&amp;gt;vatrate, $obj-&amp;gt;localtax1_tx, $obj-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $obj-&amp;gt;info_bits, $obj-&amp;gt;product_type, $seller, $localtax_array);&lt;br /&gt;
        $diff=price2num($tmpcal[1] - $obj-&amp;gt;total_tva, &#039;MT&#039;, 1);&lt;br /&gt;
        if ($diff)&lt;br /&gt;
        {&lt;br /&gt;
            $sqlfix=&amp;quot;UPDATE &amp;quot;.MAIN_DB_PREFIX.$this-&amp;gt;table_element_line.&amp;quot; SET &amp;quot;.$fieldtva.&amp;quot; = &amp;quot;.$tmpcal[1].&amp;quot;, total_ttc = &amp;quot;.$tmpcal[2].&amp;quot; WHERE rowid = &amp;quot;.$obj-&amp;gt;rowid;&lt;br /&gt;
            dol_syslog(&#039;We found unconsistent data into detailed line (difference of &#039;.$diff.&#039;) for line rowid = &#039;.$obj-&amp;gt;rowid.&amp;quot; (total vat of line calculated=&amp;quot;.$tmpcal[1].&amp;quot;, database=&amp;quot;.$obj-&amp;gt;total_tva.&amp;quot;). We fix the total_vat and total_ttc of line by running sqlfix = &amp;quot;.$sqlfix);&lt;br /&gt;
            $resqlfix=$this-&amp;gt;db-&amp;gt;query($sqlfix);&lt;br /&gt;
            if (! $resqlfix) dol_print_error($this-&amp;gt;db,&#039;Failed to update line&#039;);&lt;br /&gt;
            $obj-&amp;gt;total_tva = $tmpcal[1];&lt;br /&gt;
            $obj-&amp;gt;total_ttc = $tmpcal[2];&lt;br /&gt;
            //&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just after, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
$cf_coeff = $cf_records[$obj-&amp;gt;rowid]-&amp;gt;coefficient;&lt;br /&gt;
if (empty($cf_coeff) and $cf_coeff !== 0) $cf_coeff = 1; // by default, an empty coefficient = 1&lt;br /&gt;
$obj-&amp;gt;total_ht = $obj-&amp;gt;total_ht * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_tva = $obj-&amp;gt;total_tva * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_localtax1 = $obj-&amp;gt;total_localtax1 * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_localtax2 = $obj-&amp;gt;total_localtax2 * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_ttc = $obj-&amp;gt;total_ttc * $cf_coeff;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should do the trick. Now go to one of your invoices (or whatever module you are targetting), and you should see this:&lt;br /&gt;
[[File:Cf_wiki_coeff_remaining_debt.png]]&lt;br /&gt;
&lt;br /&gt;
= Custom tax per product =&lt;br /&gt;
&lt;br /&gt;
This part will show you how to create a static custom tax per product. Static means that the tax will be stored per product, and not per product&#039;s line, thus you just have to setup the tax once per product and it will always be applied in every invoice you add this product.&lt;br /&gt;
&lt;br /&gt;
To do that, you will need the CustomFields Pro module + ProductsEasyAccess.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
1- Create a product&#039;s custom fields called &amp;quot;custom_tax&amp;quot;. Now, using ProductsEasyAccess, you can access this field wherever you wants, either to show it on the Dolibarr&#039;s interface (see previous chapter), or in a PDF template, which we will describe below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2- Modify your invoice&#039;s PDF template. You first need to load all the products&#039; fields &#039;&#039;before&#039;&#039; the loop over the products&#039; lines. To do this, search for a line like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// This should be placed above the &amp;quot;loop lines&amp;quot; below, add this:&lt;br /&gt;
&lt;br /&gt;
dol_include_once(&#039;/productseasyaccess/lib/productseasyaccess.lib.php&#039;);&lt;br /&gt;
fill_object_with_products_fields($object);&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines, search for this command&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  // Add this just below the for loop, this will ease your php manipulation after.&lt;br /&gt;
  // This will allow you to more easily access the products&#039; fields (this will store the id of each product inside $lineproductid).&lt;br /&gt;
  $line_product_id = $object-&amp;gt;lines[$i]-&amp;gt;fk_product;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4- You can now use your products&#039; fields in your PDF. To access the custom_tax field, just use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And to print it in your PDF:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell(0,3, $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax, 0, &#039;L&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5- Then, if you want to count the custom tax total and add it to your vat total, you can create a counter inside the for loop we cited in the first step:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_custom_tax = 0; // Initialize the custom tax total counter&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  $object-&amp;gt;total_custom_tax += $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then find inside the function **_tableau_tot**, find the following line:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And prepend the total modification like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc + $object-&amp;gt;total_custom_tax; // prepend this to add the custom tax to the total&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Done. This should show your custom tax per product and your total including all custom taxes.&lt;/div&gt;</summary>
		<author><name>Lrq3000</name></author>
	</entry>
	<entry>
		<id>https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33050</id>
		<title>Module CustomFields Cases</title>
		<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33050"/>
		<updated>2015-01-07T19:01:45Z</updated>

		<summary type="html">&lt;p&gt;Lrq3000: /* Implementing the change of price in remaining debt to pay */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:CustomFields]]&lt;br /&gt;
{{TemplateDocDevEn}}&lt;br /&gt;
{{TemplateModEN}}&lt;br /&gt;
{{BasculeDevUserEn|&lt;br /&gt;
name=CustomFields|&lt;br /&gt;
num=8500|&lt;br /&gt;
devdoc=[[Module_CustomFields_Dev]]|&lt;br /&gt;
userdoc=[[Module_CustomFields]]|}}&lt;br /&gt;
This page lists a compilation of concrete, practical examples uses of [[Module_CustomFields|CustomFields]] and of its various features.&lt;br /&gt;
&lt;br /&gt;
If you had an experience with CustomFields, you can also add here your case as an example for others to get inspired.&lt;br /&gt;
&lt;br /&gt;
= How to make a generic PDF template listing all custom fields =&lt;br /&gt;
&lt;br /&gt;
To make a generic PDF template listing all custom fields, you just need to take an existing PDF template, then you need to follow these 3 steps:&lt;br /&gt;
&lt;br /&gt;
1/ Edit the class name (required, else it will conflict with the original template).&lt;br /&gt;
&lt;br /&gt;
Eg: if we take the crabe template for invoices, we change:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;crabe&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
into:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_customfields extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;customfields&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2/ Copy this generic function at the end of the PDF:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propale/order/etc... (CustomFields simpler functions will automatically adapt)&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs); // set default PDF font size&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
    // Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
    $customfields = customfields_fill_object($object, null, $outputlangs, null, true);&lt;br /&gt;
&lt;br /&gt;
    // Setting the starting position of the text cursor&lt;br /&gt;
    $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
    $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
    // Printing the customfields&lt;br /&gt;
    foreach ($object-&amp;gt;customfields as $label=&amp;gt;$value) { // $value is already formatted!&lt;br /&gt;
        // Get translated label&lt;br /&gt;
        $translatedlabel = $customfields-&amp;gt;findLabelPDF($label, $outputlangs); // translated label of the customfield (not translated by default in customfields_fill_object() because a field should always be accessible by a base name, whatever the translation is)&lt;br /&gt;
&lt;br /&gt;
        // PDF formatting, placement and printing&lt;br /&gt;
        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
        $pdf-&amp;gt;MultiCell(0,3, $translatedlabel.&#039; ($object-&amp;gt;customfields-&amp;gt;&#039;.$label.&#039;): &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternative function (with a bit less functionalities, but more controllable):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page (used to debug if CustomFields setup is correct)&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propal/product/whatever...&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs);&lt;br /&gt;
&lt;br /&gt;
            if (empty($object-&amp;gt;table_element) or empty($object-&amp;gt;id)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;Current \$object is not compatible with CustomFields, could not find table_element or id.&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
    $customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element);&lt;br /&gt;
&lt;br /&gt;
            // Fetching custom fields records&lt;br /&gt;
            $fields = $customfields-&amp;gt;fetch($object-&amp;gt;id);&lt;br /&gt;
&lt;br /&gt;
            if (!isset($fields)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;No custom field could be found for this object. Please check your configuration (did you create at least one customfield and set a value in the current datasheet?)&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            } else {&lt;br /&gt;
                // Setting the starting position of the text cursor&lt;br /&gt;
                $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
                $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
                // Printing the customfields&lt;br /&gt;
                foreach ($fields as $key=&amp;gt;$field) {&lt;br /&gt;
                        $translatedname = $customfields-&amp;gt;findLabelPDF($key, $outputlangs); // label of the customfield&lt;br /&gt;
                        $value = $customfields-&amp;gt;simpleprintFieldPDF($key, $field, $outputlangs); // value (cleaned and properly formatted) of the customfield&lt;br /&gt;
&lt;br /&gt;
                        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
                        $pdf-&amp;gt;MultiCell(0,3, $translatedname.&#039;: &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
                        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3/ Call the _pagecustomfields function at the right place (generally, after _pagefoot and just before $pdf-&amp;gt;Close()):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Pied de page&lt;br /&gt;
$this-&amp;gt;_pagefoot($pdf,$object,$outputlangs);&lt;br /&gt;
$pdf-&amp;gt;AliasNbPages();&lt;br /&gt;
&lt;br /&gt;
// CustomFields&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) { // if the customfields module is activated...&lt;br /&gt;
        // Add a page with the customfields&lt;br /&gt;
        $pdf-&amp;gt;AddPage();&lt;br /&gt;
        $this-&amp;gt;_pagecustomfields($pdf,$object,$outputlangs);&lt;br /&gt;
        $pagenb++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
$pdf-&amp;gt;Close(); // you should first search for this, you should only find one occurrence&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then go to the module&#039;s admin panel (eg: invoice admin panel) and enable the template you&#039;ve just created.&lt;br /&gt;
&lt;br /&gt;
You can now use it to test if your customfields work.&lt;br /&gt;
&lt;br /&gt;
= Conditions on fields selection =&lt;br /&gt;
&lt;br /&gt;
Sometimes you may want to show a DropdownBox or Constraint (or another multi-choice type), but you may want to show or hide choices based on conditions (eg: only if it&#039;s a prospect, only if this zone was selected, only if this category was selected, etc..).&lt;br /&gt;
&lt;br /&gt;
To know which is the best solution you should choose to implement your condition, you should first know in which category your condition falls in.&lt;br /&gt;
&lt;br /&gt;
== Conditions categories ==&lt;br /&gt;
&lt;br /&gt;
Conditions can be categorized into 4 types:&lt;br /&gt;
&lt;br /&gt;
* static condition: choices are restrained on a condition that never changes (eg: only show third-parties that are suppliers, or lowercase all data on sql insertion, or check that a field is above a certain number, etc.).&lt;br /&gt;
* semi-dynamic condition: choices that are constrained on a static table but based on the value of another field/customfield. This is also commonly called &amp;quot;Cascaded dropdown lists&amp;quot; or &amp;quot;Dynamically linked dropdown lists&amp;quot;.&lt;br /&gt;
* dynamic: everything is dynamic: choices are based on a subfield of a field of the current module, so that there&#039;s no material table and it&#039;s impossible to create a view (because you would need to create one view per id) (eg: show only the contacts attached to the third-party being accessed)&lt;br /&gt;
* timed condition: a condition that is based on time or recurrence (eg: at 5:00 AM delete old records, etc..)&lt;br /&gt;
&lt;br /&gt;
== Conditions solutions ==&lt;br /&gt;
&lt;br /&gt;
Now that you know what kind of condition you want to set, you can choose one of the following solutions depending on your preferences and knowledge.&lt;br /&gt;
&lt;br /&gt;
In any case, you can always use the &#039;&#039;&#039;CustomFields&#039;s overload functions&#039;&#039;&#039; to fit your needs for nearly every type of condition.&lt;br /&gt;
&lt;br /&gt;
=== Static condition ===&lt;br /&gt;
&lt;br /&gt;
* View: create a view on the table based on the condition, and then create a custom field constrained on this view (which is just like any table), eg: only show third-parties that are suppliers:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
CREATE VIEW llx_societe_supplier_view AS&lt;br /&gt;
SELECT *&lt;br /&gt;
FROM llx_societe&lt;br /&gt;
WHERE fournisseur=1;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
WARNING: foreign keys (and thus constraints) can&#039;t be used on views with MySQL, and materialized views neither exist, so you can&#039;t use this method with MySQL.&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
* SQL Transformations&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
=== Semi-dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade option is ideal in most cases, and if not enough, you can use the Cascade Custom option and Overloading Functions.&lt;br /&gt;
* View&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
&lt;br /&gt;
=== Dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade Custom and Overload functions&lt;br /&gt;
* Making your own module, which calls the CustomFields class&lt;br /&gt;
&lt;br /&gt;
=== Timed condition ===&lt;br /&gt;
&lt;br /&gt;
* SQL scheduled events&lt;br /&gt;
* Cron job&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
= Overloading functions =&lt;br /&gt;
&lt;br /&gt;
Here are a few concrete use cases of overloading functions to achieve various goals.&lt;br /&gt;
&lt;br /&gt;
== Dynamic list selection based on another one ==&lt;br /&gt;
&lt;br /&gt;
The goal is to show two custom fields on the Third-Party module: one which gives the zone (secteur) of the third-party (Isere, Alpes du sud, Haute-Savoie...) and the other which gives relative to the zone the list of all the ski resorts (station_a) inside the selected zone.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will show how to manually manage this kind of requirements using [[Module_CustomFields#Overloading_functions:_Adding_your_own_management_code_for_custom_fields|overloading functions]], but in latest CustomFields releases, the same solution can be achieved automatically using the [[Module_CustomFields#Cascade|Cascade option]].&lt;br /&gt;
&lt;br /&gt;
We have one table &#039;&#039;&#039;llx_k_station&#039;&#039;&#039; (rowid, station (char50), secteur(char50)) which contains the following: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1,les 2 alpes,isère&lt;br /&gt;
2,chamrousse,isère&lt;br /&gt;
3,alpe d&#039;huez,isère&lt;br /&gt;
4,vars,alpes du sud&lt;br /&gt;
5,risoul,alpes du sud&lt;br /&gt;
etc...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In CustomFields&#039;s admin panel, we create two custom fields:&lt;br /&gt;
&lt;br /&gt;
1- secteur: returns the list of all zones - type DropdownBox: enum(&#039;- Aucun&#039;,&#039;Alpes du Sud&#039;,&#039;export&#039;,&#039;Haute-Savoie&#039;,&#039;Isère&#039;, etc...)&lt;br /&gt;
&lt;br /&gt;
2- station_a: returns the list of all ski resorts - type constraint on &#039;&#039;&#039;llx_k_station&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Here is the code to put in customfields_fields_extend.lib.php that will allow to show only the ski resorts corresponding to the selected zone:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_editview_societe_station_a (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$parameters, &amp;amp;$action, &amp;amp;$user, &amp;amp;$idvar, &amp;amp;$rightok, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
  global $db; // allow to access database functions&lt;br /&gt;
  $sql=&amp;quot;SELECT llx_k_station.station, llx_k_station.rowid FROM llx_societe_customfields INNER JOIN llx_k_station ON llx_societe_customfields.Secteur = llx_k_station.secteur WHERE llx_societe_customfields.fk_societe=&amp;quot;.$object-&amp;gt;id;&lt;br /&gt;
$result=$db-&amp;gt;query($sql);&lt;br /&gt;
 &lt;br /&gt;
  if ($result) {&lt;br /&gt;
    $num = $db-&amp;gt;num_rows($result);&lt;br /&gt;
    $i = 0;&lt;br /&gt;
    $myarray = array();&lt;br /&gt;
    while ($i &amp;lt; $num)&lt;br /&gt;
    {&lt;br /&gt;
      $obj = $db-&amp;gt;fetch_object($result);&lt;br /&gt;
      $myarray []=array(&#039;id&#039;=&amp;gt;$obj-&amp;gt;rowid, &#039;value&#039;=&amp;gt;$obj-&amp;gt;station);&lt;br /&gt;
      $i++;&lt;br /&gt;
    }&lt;br /&gt;
    $value = $myarray; &lt;br /&gt;
    $db-&amp;gt;free($result);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to manub for giving this case.&lt;br /&gt;
&lt;br /&gt;
== Dynamic discount computation per products lines on propales ==&lt;br /&gt;
&lt;br /&gt;
The goal here is to compute the discount for a product line in propales based on a computation on three custom fields.&lt;br /&gt;
&lt;br /&gt;
First, create three custom fields for &amp;quot;Line commercial proposals&amp;quot;: vl, cpl and lc.&lt;br /&gt;
&lt;br /&gt;
Then add the following overloading function at the end of your customfields_fields_extend.lib.php file (read the comments for more informations):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_propaldet_vl (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
&lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $vl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;vl&#039;};&lt;br /&gt;
    $cpl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;cpl&#039;};&lt;br /&gt;
    $lc = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;lc&#039;};&lt;br /&gt;
&lt;br /&gt;
    // Compute the discount&lt;br /&gt;
    $remise_calculee = (100 - $vl) * (100 - $cpl) * (100 - $lc);&lt;br /&gt;
&lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $remise_calculee = price2num($remise_calculee);&lt;br /&gt;
&lt;br /&gt;
    // Assign the new discount into the PropalLine object&lt;br /&gt;
    $object-&amp;gt;remise_percent = $remise_calculee;&lt;br /&gt;
&lt;br /&gt;
    // Update the price (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
&lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
&lt;br /&gt;
    //$q = &amp;quot;UPDATE  &amp;quot;.MAIN_DB_PREFIX.&amp;quot;propaldet SET remise_percent=&amp;quot;.$remise_calculee.&amp;quot; WHERE  rowid=&amp;quot;.$object-&amp;gt;rowid;&lt;br /&gt;
    //$db-&amp;gt;query($q);&lt;br /&gt;
    &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;vl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;cpl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;lc&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Linking Dolibarr objects from two different modules =&lt;br /&gt;
&lt;br /&gt;
Dolibarr sometimes offers the possibility to link two different modules. Eg: when you convert an Intervention card into an Invoice.&lt;br /&gt;
&lt;br /&gt;
In this case, you might want in your templates to fetch the custom fields of the linked object. Eg: you generate a PDF from the Invoice, but you want to get the custom fields from the Intervention that was converted into the Invoice.&lt;br /&gt;
&lt;br /&gt;
The key here is to use the &#039;&#039;&#039;llx_element_element&#039;&#039;&#039; table or the &#039;&#039;&#039;llx_element_*&#039;&#039;&#039; tables (eg: llx_element_contact): this is a standard Dolibarr table that stores all links between objects of two different modules.&lt;br /&gt;
&lt;br /&gt;
Here is the structure:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
rowid | fk_source | sourcetype | fk_target | targettype&lt;br /&gt;
-------------------------------------------------------&lt;br /&gt;
1 | 2 | fichinter | 5 | facture&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can then use a php code like the following to populate your $object with customfields:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
// This will also create and returns us an instance of the CustomFields class, which we will use to manually query the llx_element_element table&lt;br /&gt;
$customfields = customfields_fill_object($object, null, $outputlangs, &#039;facture&#039;, true); // beautified values&lt;br /&gt;
&lt;br /&gt;
// Querying the llx_element_element table manually to get the link between the invoice and the linked intervention card (we want the id of the intervention card)&lt;br /&gt;
// fetchAny($columns, $table, $where=&#039;&#039;, $orderby=&#039;&#039;, $limitby=&#039;&#039;)&lt;br /&gt;
$eltrow = $customfields-&amp;gt;fetchAny(&#039;*&#039;, MAIN_DB_PREFIX.&#039;element_element&#039;, &#039;targettype=&amp;quot;&#039;.$object-&amp;gt;element.&#039;&amp;quot; and fk_target=&#039;.$object-&amp;gt;id.&#039; and sourcetype=&amp;quot;fichinter&amp;quot;&#039;);&lt;br /&gt;
&lt;br /&gt;
// Save the intervention&#039;s id&lt;br /&gt;
$sourceid = $eltrow[0][&#039;fk_source&#039;];&lt;br /&gt;
&lt;br /&gt;
// Crafting a dummy intervention object&lt;br /&gt;
$fromobject = new stdClass(); // Init an empty object&lt;br /&gt;
$fromobject-&amp;gt;id = $sourceid; // We need the id and..&lt;br /&gt;
$fromobject-&amp;gt;table_element = &#039;fichinter&#039;; // the table_element (name of the table)&lt;br /&gt;
&lt;br /&gt;
// We can then fill $object with the intervention custom fields (we store them in a sub property &#039;fichinter&#039; to avoid conflicts with invoice&#039;s custom fields)&lt;br /&gt;
customfields_fill_object($object, $fromobject, $outputlangs, &#039;fichinter&#039;, true);&lt;br /&gt;
&lt;br /&gt;
// Now you can access invoices custom fields using:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;facture-&amp;gt;cf_myfield);&lt;br /&gt;
// And access intervention custom fields with:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;fichinter-&amp;gt;cf_myfield);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to netassopro for the tip.&lt;br /&gt;
&lt;br /&gt;
= Modify the total price of every product/service line with a coefficient (without tampering the discount) =&lt;br /&gt;
&lt;br /&gt;
Here we will go through the creation and management of a custom field called &#039;&#039;&#039;coefficient&#039;&#039;&#039; that will change the total price of each product by modifying it by the coefficient.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will not create a coefficient per product but rather per product&#039;s line. Thus if you want to create a coefficient that never change between invoices but only depending on the product and stays static, please refer to the next chapter below about creating a custom tax per product.&lt;br /&gt;
&lt;br /&gt;
The preliminary and necessary step is to first create a custom field that will be used to modify the total price. We will create one for Client Invoice Lines.&lt;br /&gt;
&lt;br /&gt;
Go to the CustomFields administration panel, then into the Client Invoice Lines tab, then click on the button New Field, name the field &#039;&#039;&#039;coefficient&#039;&#039;&#039;, and set the type to &#039;&#039;&#039;Double&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
When you go to client invoices in Dolibarr, you should then see something similar to this:&lt;br /&gt;
[[File:Cfe1.jpg]]&lt;br /&gt;
You can see that our coefficient field was successfully created.&lt;br /&gt;
&lt;br /&gt;
Then here are two alternative ways of implementing the change of the total price of each product relativery to this coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change total price in database ==&lt;br /&gt;
&lt;br /&gt;
Here we will use a &amp;quot;save&amp;quot; overloading function to change the total price directly in the database, thus we won&#039;t have to modify anything else (eg: nor PDF templating nor ODT) since the total price will be already updated in the database.&lt;br /&gt;
&lt;br /&gt;
Warning: this method will work only with CustomFields =&amp;gt; 3.2.23 since the &amp;quot;save&amp;quot; overloading function did not work for products lines before.&lt;br /&gt;
&lt;br /&gt;
Just add the following overloading function in your customfields_fields_extend.lib.php file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_facturedet_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
 &lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $coefficient = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;coefficient&#039;}; // you could also use $coefficient = $value;&lt;br /&gt;
 &lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $coefficient = price2num($coefficient);&lt;br /&gt;
 &lt;br /&gt;
    // Get the original subprices (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
 &lt;br /&gt;
    // Update the subprices with the coefficient&lt;br /&gt;
    $object-&amp;gt;total_ht = $object-&amp;gt;total_ht * $coefficient; // compute no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva = $object-&amp;gt;total_tva * $coefficient; // compute tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc * $coefficient; // compute tax included price&lt;br /&gt;
 &lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(&#039;&#039;,true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
 &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;coefficient&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should show the correct total price on the Dolibarr&#039;s interface without modifying the unit subprice (P.U. Unit):&lt;br /&gt;
&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
You have nothing to edit in your PDF or ODT template, since the new subprice with applied coefficient will be automatically printed instead of the old one without coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change printed total price per product without tampering the subprice in the database ==&lt;br /&gt;
&lt;br /&gt;
Here is a way to change the printed total price (the one shown on screen and in PDF/ODT) but without changing the total price in the database.&lt;br /&gt;
&lt;br /&gt;
The following sub-chapters are all independent, thus you can only follow the ones that you require, eg: if you only need to change the price in your PDF documents, just create a custom field then jump to the sub-chapter to &#039;&#039;&#039;Implement the change of price in PDF&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in ODT templates ===&lt;br /&gt;
&lt;br /&gt;
There&#039;s no eval implementation currently inside ODT templates, but there is a kind of hook implementation, called [[Create_an_ODT_document_template#Other_personalized_tags|substitution functions]].&lt;br /&gt;
&lt;br /&gt;
You can either create your own module, then create your own substitution function, but for the sake of simplicity in this tutorial, we will directly use the CustomFields substitution function. However, please remember that you will have to redo your changes everytime you will update the CustomFields module, so if you don&#039;t want to do that it would be better if you create your own module and substitution function.&lt;br /&gt;
&lt;br /&gt;
Open the file &#039;&#039;&#039;htdocs/customfields/core/substitutions/functions_customfields.lib.php&#039;&#039;&#039; and go to the function &#039;&#039;&#039;customfields_completesubstitutionarray_lines&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
At the very end of this function, but inside the customfields check:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) {&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
But just before the ending bracket, you should add the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($object-&amp;gt;table_element_line === &#039;facturedet&#039;) { // First check that we are in the right module, we don&#039;t want to use coefficient for other modules if we didn&#039;t define the custom field there&lt;br /&gt;
    $substitutionarray[&#039;line_price_full&#039;] = $substitutionarray[&#039;line_price_ttc&#039;] * $substitutionarray[&#039;cf_coefficient&#039;]; // multiply the total price with our coefficient&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code will add a new tag &#039;&#039;&#039;{line_price_full}&#039;&#039;&#039; that you can then use inside your ODT templates.&lt;br /&gt;
&lt;br /&gt;
Just place this tag in a table in your ODT document, between the table tags ( [!-- BEGIN row.lines --] ... [!-- END row.lines --] ), and this will print your full price.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in PDF templates ===&lt;br /&gt;
&lt;br /&gt;
First, we will create our own PDF template by copying an existing template, and then we will edit it to change the price of items.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Creating your own PDF template by copying an existing template&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Go into the following folder: &#039;&#039;&#039;htdocs/core/modules/facture/doc/&#039;&#039;&#039;, then copy the file &#039;&#039;&#039;pdf_crabe.modules.php&#039;&#039;&#039; and name the new one &#039;&#039;&#039;pdf_crabecoeff.modules.php&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Then open it in your favourite editor, and change the following lines:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures // change pdf_crabe into pdf_crabecoeff&lt;br /&gt;
{&lt;br /&gt;
...&lt;br /&gt;
    function __construct($db)&lt;br /&gt;
    {&lt;br /&gt;
        ...&lt;br /&gt;
	$this-&amp;gt;name = &amp;quot;crabe&amp;quot;; // change &amp;quot;crabe&amp;quot; into &amp;quot;crabecoeff&amp;quot;&lt;br /&gt;
        ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you have your own PDF template!&lt;br /&gt;
&lt;br /&gt;
Now you should &#039;&#039;&#039;enable your PDF template&#039;&#039;&#039; before doing anything else, by going into the Modules Administration panel, and into the Invoice admin page, then click on the ON button at the right of the &#039;&#039;&#039;crabecoeff&#039;&#039;&#039; entry to enable the template.&lt;br /&gt;
&lt;br /&gt;
Now, let&#039;s proceed onto modifying the prices.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Changing the prices automatically in your PDF&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
You can now do pretty much anything you want in your PDF template, but I will give you a mean to automatically change the prices of every products with our coefficient, as well as the total price, without editing too much the template.&lt;br /&gt;
&lt;br /&gt;
First, try to find the following code of line (should be inside the write_file() method):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf=pdf_getInstance($this-&amp;gt;format);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, just copy/paste the following code &#039;&#039;&#039;below&#039;&#039;&#039; pdf_getInstance():&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$this-&amp;gt;customfields = customfields_fill_object($object, null, $outputlangs, null, true); // beautified values&lt;br /&gt;
$this-&amp;gt;customfields_raw = customfields_fill_object($object, null, $outputlangs, &#039;raw&#039;, null); // raw values&lt;br /&gt;
$this-&amp;gt;customfields_lines = customfields_fill_object_lines($object, null, $outputlangs, null, true); // product lines&#039; values&lt;br /&gt;
&lt;br /&gt;
// Resetting every $object price because we will compute them anew&lt;br /&gt;
$object-&amp;gt;total_ht = 0;&lt;br /&gt;
$object-&amp;gt;total_tva = 0;&lt;br /&gt;
$object-&amp;gt;total_ttc = 0;&lt;br /&gt;
// For each line of product/service, we will recompute all the prices&lt;br /&gt;
foreach($object-&amp;gt;lines as $line) {&lt;br /&gt;
    // Get the coefficient custom field&#039;s value for the current line&lt;br /&gt;
    $coeff = $object-&amp;gt;customfields-&amp;gt;lines-&amp;gt;{$line-&amp;gt;rowid}-&amp;gt;cf_coefficient;&lt;br /&gt;
    // Recompute the price of this product/service line&lt;br /&gt;
    $line-&amp;gt;total_ht = $line-&amp;gt;total_ht*$coeff; // compute no tax price&lt;br /&gt;
    $line-&amp;gt;total_tva = $line-&amp;gt;total_tva*$coeff; // compute tax amount&lt;br /&gt;
    $line-&amp;gt;total_ttc = $line-&amp;gt;total_ttc*$coeff; // compute tax included price&lt;br /&gt;
    // Do the sum of all new products/services prices and tva (will be incremented with every product/service line)&lt;br /&gt;
    $object-&amp;gt;total_ht += $line-&amp;gt;total_ht; // global no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva += $line-&amp;gt;total_tva; // global vat/tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc += $line-&amp;gt;total_ttc; // global  tax included price&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you should try to generate your PDF document, and you should get something like this:&lt;br /&gt;
[[File:Cfe3.jpg]]&lt;br /&gt;
&lt;br /&gt;
Note: printing the coefficient as a column in your PDF document is also possible but will not be covered here because PDF templates will vary for every persons depending on your needs.&lt;br /&gt;
For guidelines about how to do this, you can read the documentation on [[Create_a_PDF_document_template|how to create a PDF template]] and [[Module_CustomFields#Implementing_in_PDF_templates|how to use custom fields in PDF templates]].&lt;br /&gt;
&lt;br /&gt;
Hint: you should be looking inside the following conditional block, inside the write_file() method:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
{&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of total price in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
Unluckily there is not (yet) any hooking context to modify how lines of products/services are printed, thus the only thing you can do is directly edit the &#039;&#039;&#039;template file&#039;&#039;&#039; and manually put inside your code to load your custom field and print it the way you want using HTML formatting, for our example:&lt;br /&gt;
&lt;br /&gt;
* For Dolibarr 3.3 and above, edit the file &#039;&#039;&#039;htdocs/core/tpl/objectline_view.tpl.php&#039;&#039;&#039;&lt;br /&gt;
* For Dolibarr 3.1 and 3.2, edit the file &#039;&#039;&#039;htdocs/core/tpl/freeproductline_view.tpl.php&#039;&#039;&#039; AND &#039;&#039;&#039;predefinedproductline_view.tpl.php&#039;&#039;&#039; (apply the same changes to both files)&lt;br /&gt;
&lt;br /&gt;
In the template file you opened, find the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And replace it by:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $object-&amp;gt;table_element_line; // you can use $object-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
&lt;br /&gt;
// Modifying the HT price and printing it on the Dolibarr&#039;s datasheet interface&lt;br /&gt;
// Applying a multiplying/dividing coefficient to an HT price will give a mathematically correct TTC price since the taxes are also a multiplier&lt;br /&gt;
// However, if you want to apply other mathematical operations (like addition or substaction), you should use the total price with $line-&amp;gt;total_ttc&lt;br /&gt;
if (!empty($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
    echo price($line-&amp;gt;total_ht * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient);&lt;br /&gt;
} else {&lt;br /&gt;
    echo price($line-&amp;gt;total_ht);&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should produce something like this:&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Technical note: here we are using customfields_fill_object() instead of customfields_fill_object_lines() (the latter being specifically adapted to lines and thus should theoretically be used here) for performance reasons because the template file we are editing is called in a loop, and customfields_fill_object_lines() should be called outside a loop (because it already does the job that is done by the loop), so here we &#039;tricks&#039; the customfields_fill_object() function to avoid this loop by submitting &#039;facturedet&#039; as a module by itself (when in fact it&#039;s a product lines table, but it works the same way nevertheless). This is a good demonstration of the flexibility of the CustomFields functions and methods.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the coefficient column in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
It&#039;s also possible to show the coefficient in a new column on the Dolibarr interface.&lt;br /&gt;
&lt;br /&gt;
To do that, you need to do two things:&lt;br /&gt;
&lt;br /&gt;
1- Add the column title &amp;quot;Coeff.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
2- Add the generator that will input the coefficient value for each line.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Add the column title &amp;quot;Coeff.&amp;quot;&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
Open in your favourite editor the file /htdocs/core/class/commonobject.class.php and inside the function printObjectLines (note the plural s), find the following lines of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Total HT&lt;br /&gt;
print &#039;&amp;lt;td align=&amp;quot;right&amp;quot; width=&amp;quot;50&amp;quot;&amp;gt;&#039;.$langs-&amp;gt;trans(&#039;TotalHTShort&#039;).&#039;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is what prints the Total HT price column. We will place the Coeff column just before.&lt;br /&gt;
&lt;br /&gt;
So, just above this code, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
dol_include_once(&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element_line);&lt;br /&gt;
$coeff_field = $customfields-&amp;gt;fetchFieldStruct(&#039;coefficient&#039;);&lt;br /&gt;
if (!empty($coeff_field)) {&lt;br /&gt;
    print &#039;&amp;lt;td align=&amp;quot;right&amp;quot; width=&amp;quot;50&amp;quot;&amp;gt;&#039;.$langs-&amp;gt;trans(&#039;Coeff.&#039;).&#039;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will take care of showing the Coeff. column, but only for modules where you created such a custom field (because the file we modified is a common object for all modules in Dolibarr, not just invoices, thus this modification will gracefully skip this custom field if it does not exists for other modules).&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Add the generator&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/tpl/objectline_view.tpl.php and try to find the following block of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php if ($line-&amp;gt;special_code == 3)	{ ?&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo $langs-&amp;gt;trans(&#039;Option&#039;); ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } else { ?&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is what prints the total HT price value for each line.&lt;br /&gt;
&lt;br /&gt;
Just above of this block of code, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $object-&amp;gt;table_element_line; // you can use $this-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
if (isset($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
    &amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient; ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just like for the column title, this code will take care of generating the coefficient value only if the custom field is defined, because this file is also a generic template for all modules of Dolibarr.&lt;br /&gt;
&lt;br /&gt;
At this point, everything is done, you should see the following:&lt;br /&gt;
[[File:Cf_wiki_coeff_column.png]]&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in margin module ===&lt;br /&gt;
&lt;br /&gt;
If you enabled the margin module, you should see that the coefficient is not applied there, but it&#039;s easy to fix that.&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/class/commonobject.class.php and find the function getMarginInfos(), then inside find the following lines:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach($this-&amp;gt;lines as $line) {&lt;br /&gt;
    if (empty($line-&amp;gt;pa_ht) &amp;amp;&amp;amp; isset($line-&amp;gt;fk_fournprice) &amp;amp;&amp;amp; !$force_price) {&lt;br /&gt;
        $product = new ProductFournisseur($this-&amp;gt;db);&lt;br /&gt;
        if ($product-&amp;gt;fetch_product_fournisseur_price($line-&amp;gt;fk_fournprice))&lt;br /&gt;
            $line-&amp;gt;pa_ht = $product-&amp;gt;fourn_unitprice * (1 - $product-&amp;gt;fourn_remise_percent / 100);&lt;br /&gt;
        if (isset($conf-&amp;gt;global-&amp;gt;MARGIN_TYPE) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;MARGIN_TYPE == &amp;quot;2&amp;quot; &amp;amp;&amp;amp; $product-&amp;gt;fourn_unitcharges &amp;gt; 0)&lt;br /&gt;
            $line-&amp;gt;pa_ht += $product-&amp;gt;fourn_unitcharges;&lt;br /&gt;
    }&lt;br /&gt;
    // si prix d&#039;achat non renseigné et devrait l&#039;être, alors prix achat = prix vente&lt;br /&gt;
    if ((!isset($line-&amp;gt;pa_ht) || $line-&amp;gt;pa_ht == 0) &amp;amp;&amp;amp; $line-&amp;gt;subprice &amp;gt; 0 &amp;amp;&amp;amp; (isset($conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull == 1)) {&lt;br /&gt;
        $line-&amp;gt;pa_ht = $line-&amp;gt;subprice * (1 - ($line-&amp;gt;remise_percent / 100));&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the lines are filled with margin infos. The price has just been fetched here, and we will now modify it with the coefficient before the price gets used for margin computation.&lt;br /&gt;
&lt;br /&gt;
Just below the code we found, paste the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $line-&amp;gt;table_element;&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
if (!empty($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
    $line-&amp;gt;pa_ht = $line-&amp;gt;pa_ht * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient;&lt;br /&gt;
    $line-&amp;gt;subprice = $line-&amp;gt;subprice * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s all, the price should be reflected in the margin.&lt;br /&gt;
[[File:Cf_wiki_coeff_margin.png]]&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in remaining debt to pay ===&lt;br /&gt;
&lt;br /&gt;
Open /htdocs/core/class/commonobject.class.php and look for the function update_price().&lt;br /&gt;
&lt;br /&gt;
You should see the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function update_price($exclspec=0,$roundingadjust=&#039;none&#039;,$nodatabaseupdate=0,$seller=&#039;&#039;)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just below the include_once line, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;;&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/conf/conf_customfields_func.lib.php&#039;;&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $this-&amp;gt;table_element_line);&lt;br /&gt;
// Fetching line ids from parent object id&lt;br /&gt;
$line_ids = $customfields-&amp;gt;fetchAny(&#039;rowid&#039;, MAIN_DB_PREFIX.$this-&amp;gt;table_element_line, $this-&amp;gt;fk_element.&#039; = &#039;.$this-&amp;gt;id);&lt;br /&gt;
$line_ids = array_values_recursive(&#039;rowid&#039;, $line_ids);&lt;br /&gt;
// Load custom fields&lt;br /&gt;
$cf_records = $customfields-&amp;gt;fetch($line_ids);&lt;br /&gt;
// Reassociate the records with the line id (instead of the customfields record id)&lt;br /&gt;
$cf_line_cfrowid_to_rowid = array_reassociate(&#039;rowid&#039;, &#039;fk_&#039;.$this-&amp;gt;table_element_line, $cf_records);&lt;br /&gt;
$tmp = $cf_records;&lt;br /&gt;
$cf_records = array();&lt;br /&gt;
foreach ($tmp as $k=&amp;gt;$v) { $cf_records[$cf_line_cfrowid_to_rowid[$k]] = $tmp[$k]; }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will load the coefficient custom field (and also any other custom field you have defined for lines).&lt;br /&gt;
&lt;br /&gt;
Now scroll below, and you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
while ($i &amp;lt; $num)&lt;br /&gt;
{&lt;br /&gt;
    $obj = $this-&amp;gt;db-&amp;gt;fetch_object($resql);&lt;br /&gt;
&lt;br /&gt;
    // Note: There is no check on detail line and no check on total, if $forcedroundingmode = &#039;none&#039;&lt;br /&gt;
&lt;br /&gt;
    if ($forcedroundingmode == &#039;0&#039;)	// Check if data on line are consistent. This may solve lines that were not consistent because set with $forcedroundingmode=&#039;auto&#039;&lt;br /&gt;
    {&lt;br /&gt;
        $localtax_array=array($obj-&amp;gt;localtax1_type,$obj-&amp;gt;localtax1_tx,$obj-&amp;gt;localtax2_type,$obj-&amp;gt;localtax2_tx);&lt;br /&gt;
        $tmpcal=calcul_price_total($obj-&amp;gt;qty, $obj-&amp;gt;up, $obj-&amp;gt;remise_percent, $obj-&amp;gt;vatrate, $obj-&amp;gt;localtax1_tx, $obj-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $obj-&amp;gt;info_bits, $obj-&amp;gt;product_type, $seller, $localtax_array);&lt;br /&gt;
        $diff=price2num($tmpcal[1] - $obj-&amp;gt;total_tva, &#039;MT&#039;, 1);&lt;br /&gt;
        if ($diff)&lt;br /&gt;
        {&lt;br /&gt;
            $sqlfix=&amp;quot;UPDATE &amp;quot;.MAIN_DB_PREFIX.$this-&amp;gt;table_element_line.&amp;quot; SET &amp;quot;.$fieldtva.&amp;quot; = &amp;quot;.$tmpcal[1].&amp;quot;, total_ttc = &amp;quot;.$tmpcal[2].&amp;quot; WHERE rowid = &amp;quot;.$obj-&amp;gt;rowid;&lt;br /&gt;
            dol_syslog(&#039;We found unconsistent data into detailed line (difference of &#039;.$diff.&#039;) for line rowid = &#039;.$obj-&amp;gt;rowid.&amp;quot; (total vat of line calculated=&amp;quot;.$tmpcal[1].&amp;quot;, database=&amp;quot;.$obj-&amp;gt;total_tva.&amp;quot;). We fix the total_vat and total_ttc of line by running sqlfix = &amp;quot;.$sqlfix);&lt;br /&gt;
            $resqlfix=$this-&amp;gt;db-&amp;gt;query($sqlfix);&lt;br /&gt;
            if (! $resqlfix) dol_print_error($this-&amp;gt;db,&#039;Failed to update line&#039;);&lt;br /&gt;
            $obj-&amp;gt;total_tva = $tmpcal[1];&lt;br /&gt;
            $obj-&amp;gt;total_ttc = $tmpcal[2];&lt;br /&gt;
            //&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just after, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
$cf_coeff = $cf_records[$obj-&amp;gt;rowid]-&amp;gt;coefficient;&lt;br /&gt;
if (empty($cf_coeff) and $cf_coeff !== 0) $cf_coeff = 1; // by default, an empty coefficient = 1&lt;br /&gt;
$obj-&amp;gt;total_ht = $obj-&amp;gt;total_ht * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_tva = $obj-&amp;gt;total_tva * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_localtax1 = $obj-&amp;gt;total_localtax1 * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_localtax2 = $obj-&amp;gt;total_localtax2 * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_ttc = $obj-&amp;gt;total_ttc * $cf_coeff;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should do the trick. Now go to one of your invoices (or whatever module you are targetting), and you should see this:&lt;br /&gt;
[[File:Cf_wiki_coeff_remaining_debt.png]]&lt;br /&gt;
&lt;br /&gt;
= Custom tax per product =&lt;br /&gt;
&lt;br /&gt;
This part will show you how to create a static custom tax per product. Static means that the tax will be stored per product, and not per product&#039;s line, thus you just have to setup the tax once per product and it will always be applied in every invoice you add this product.&lt;br /&gt;
&lt;br /&gt;
To do that, you will need the CustomFields Pro module + ProductsEasyAccess.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
1- Create a product&#039;s custom fields called &amp;quot;custom_tax&amp;quot;. Now, using ProductsEasyAccess, you can access this field wherever you wants, either to show it on the Dolibarr&#039;s interface (see previous chapter), or in a PDF template, which we will describe below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2- Modify your invoice&#039;s PDF template. You first need to load all the products&#039; fields &#039;&#039;before&#039;&#039; the loop over the products&#039; lines. To do this, search for a line like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// This should be placed above the &amp;quot;loop lines&amp;quot; below, add this:&lt;br /&gt;
&lt;br /&gt;
dol_include_once(&#039;/productseasyaccess/lib/productseasyaccess.lib.php&#039;);&lt;br /&gt;
fill_object_with_products_fields($object);&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines, search for this command&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  // Add this just below the for loop, this will ease your php manipulation after.&lt;br /&gt;
  // This will allow you to more easily access the products&#039; fields (this will store the id of each product inside $lineproductid).&lt;br /&gt;
  $line_product_id = $object-&amp;gt;lines[$i]-&amp;gt;fk_product;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4- You can now use your products&#039; fields in your PDF. To access the custom_tax field, just use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And to print it in your PDF:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell(0,3, $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax, 0, &#039;L&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5- Then, if you want to count the custom tax total and add it to your vat total, you can create a counter inside the for loop we cited in the first step:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_custom_tax = 0; // Initialize the custom tax total counter&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  $object-&amp;gt;total_custom_tax += $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then find inside the function **_tableau_tot**, find the following line:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And prepend the total modification like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc + $object-&amp;gt;total_custom_tax; // prepend this to add the custom tax to the total&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Done. This should show your custom tax per product and your total including all custom taxes.&lt;/div&gt;</summary>
		<author><name>Lrq3000</name></author>
	</entry>
	<entry>
		<id>https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33049</id>
		<title>Module CustomFields Cases</title>
		<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33049"/>
		<updated>2015-01-07T19:00:32Z</updated>

		<summary type="html">&lt;p&gt;Lrq3000: /* Implementing the change of price in margin module */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:CustomFields]]&lt;br /&gt;
{{TemplateDocDevEn}}&lt;br /&gt;
{{TemplateModEN}}&lt;br /&gt;
{{BasculeDevUserEn|&lt;br /&gt;
name=CustomFields|&lt;br /&gt;
num=8500|&lt;br /&gt;
devdoc=[[Module_CustomFields_Dev]]|&lt;br /&gt;
userdoc=[[Module_CustomFields]]|}}&lt;br /&gt;
This page lists a compilation of concrete, practical examples uses of [[Module_CustomFields|CustomFields]] and of its various features.&lt;br /&gt;
&lt;br /&gt;
If you had an experience with CustomFields, you can also add here your case as an example for others to get inspired.&lt;br /&gt;
&lt;br /&gt;
= How to make a generic PDF template listing all custom fields =&lt;br /&gt;
&lt;br /&gt;
To make a generic PDF template listing all custom fields, you just need to take an existing PDF template, then you need to follow these 3 steps:&lt;br /&gt;
&lt;br /&gt;
1/ Edit the class name (required, else it will conflict with the original template).&lt;br /&gt;
&lt;br /&gt;
Eg: if we take the crabe template for invoices, we change:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;crabe&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
into:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_customfields extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;customfields&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2/ Copy this generic function at the end of the PDF:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propale/order/etc... (CustomFields simpler functions will automatically adapt)&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs); // set default PDF font size&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
    // Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
    $customfields = customfields_fill_object($object, null, $outputlangs, null, true);&lt;br /&gt;
&lt;br /&gt;
    // Setting the starting position of the text cursor&lt;br /&gt;
    $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
    $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
    // Printing the customfields&lt;br /&gt;
    foreach ($object-&amp;gt;customfields as $label=&amp;gt;$value) { // $value is already formatted!&lt;br /&gt;
        // Get translated label&lt;br /&gt;
        $translatedlabel = $customfields-&amp;gt;findLabelPDF($label, $outputlangs); // translated label of the customfield (not translated by default in customfields_fill_object() because a field should always be accessible by a base name, whatever the translation is)&lt;br /&gt;
&lt;br /&gt;
        // PDF formatting, placement and printing&lt;br /&gt;
        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
        $pdf-&amp;gt;MultiCell(0,3, $translatedlabel.&#039; ($object-&amp;gt;customfields-&amp;gt;&#039;.$label.&#039;): &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternative function (with a bit less functionalities, but more controllable):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page (used to debug if CustomFields setup is correct)&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propal/product/whatever...&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs);&lt;br /&gt;
&lt;br /&gt;
            if (empty($object-&amp;gt;table_element) or empty($object-&amp;gt;id)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;Current \$object is not compatible with CustomFields, could not find table_element or id.&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
    $customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element);&lt;br /&gt;
&lt;br /&gt;
            // Fetching custom fields records&lt;br /&gt;
            $fields = $customfields-&amp;gt;fetch($object-&amp;gt;id);&lt;br /&gt;
&lt;br /&gt;
            if (!isset($fields)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;No custom field could be found for this object. Please check your configuration (did you create at least one customfield and set a value in the current datasheet?)&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            } else {&lt;br /&gt;
                // Setting the starting position of the text cursor&lt;br /&gt;
                $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
                $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
                // Printing the customfields&lt;br /&gt;
                foreach ($fields as $key=&amp;gt;$field) {&lt;br /&gt;
                        $translatedname = $customfields-&amp;gt;findLabelPDF($key, $outputlangs); // label of the customfield&lt;br /&gt;
                        $value = $customfields-&amp;gt;simpleprintFieldPDF($key, $field, $outputlangs); // value (cleaned and properly formatted) of the customfield&lt;br /&gt;
&lt;br /&gt;
                        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
                        $pdf-&amp;gt;MultiCell(0,3, $translatedname.&#039;: &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
                        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3/ Call the _pagecustomfields function at the right place (generally, after _pagefoot and just before $pdf-&amp;gt;Close()):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Pied de page&lt;br /&gt;
$this-&amp;gt;_pagefoot($pdf,$object,$outputlangs);&lt;br /&gt;
$pdf-&amp;gt;AliasNbPages();&lt;br /&gt;
&lt;br /&gt;
// CustomFields&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) { // if the customfields module is activated...&lt;br /&gt;
        // Add a page with the customfields&lt;br /&gt;
        $pdf-&amp;gt;AddPage();&lt;br /&gt;
        $this-&amp;gt;_pagecustomfields($pdf,$object,$outputlangs);&lt;br /&gt;
        $pagenb++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
$pdf-&amp;gt;Close(); // you should first search for this, you should only find one occurrence&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then go to the module&#039;s admin panel (eg: invoice admin panel) and enable the template you&#039;ve just created.&lt;br /&gt;
&lt;br /&gt;
You can now use it to test if your customfields work.&lt;br /&gt;
&lt;br /&gt;
= Conditions on fields selection =&lt;br /&gt;
&lt;br /&gt;
Sometimes you may want to show a DropdownBox or Constraint (or another multi-choice type), but you may want to show or hide choices based on conditions (eg: only if it&#039;s a prospect, only if this zone was selected, only if this category was selected, etc..).&lt;br /&gt;
&lt;br /&gt;
To know which is the best solution you should choose to implement your condition, you should first know in which category your condition falls in.&lt;br /&gt;
&lt;br /&gt;
== Conditions categories ==&lt;br /&gt;
&lt;br /&gt;
Conditions can be categorized into 4 types:&lt;br /&gt;
&lt;br /&gt;
* static condition: choices are restrained on a condition that never changes (eg: only show third-parties that are suppliers, or lowercase all data on sql insertion, or check that a field is above a certain number, etc.).&lt;br /&gt;
* semi-dynamic condition: choices that are constrained on a static table but based on the value of another field/customfield. This is also commonly called &amp;quot;Cascaded dropdown lists&amp;quot; or &amp;quot;Dynamically linked dropdown lists&amp;quot;.&lt;br /&gt;
* dynamic: everything is dynamic: choices are based on a subfield of a field of the current module, so that there&#039;s no material table and it&#039;s impossible to create a view (because you would need to create one view per id) (eg: show only the contacts attached to the third-party being accessed)&lt;br /&gt;
* timed condition: a condition that is based on time or recurrence (eg: at 5:00 AM delete old records, etc..)&lt;br /&gt;
&lt;br /&gt;
== Conditions solutions ==&lt;br /&gt;
&lt;br /&gt;
Now that you know what kind of condition you want to set, you can choose one of the following solutions depending on your preferences and knowledge.&lt;br /&gt;
&lt;br /&gt;
In any case, you can always use the &#039;&#039;&#039;CustomFields&#039;s overload functions&#039;&#039;&#039; to fit your needs for nearly every type of condition.&lt;br /&gt;
&lt;br /&gt;
=== Static condition ===&lt;br /&gt;
&lt;br /&gt;
* View: create a view on the table based on the condition, and then create a custom field constrained on this view (which is just like any table), eg: only show third-parties that are suppliers:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
CREATE VIEW llx_societe_supplier_view AS&lt;br /&gt;
SELECT *&lt;br /&gt;
FROM llx_societe&lt;br /&gt;
WHERE fournisseur=1;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
WARNING: foreign keys (and thus constraints) can&#039;t be used on views with MySQL, and materialized views neither exist, so you can&#039;t use this method with MySQL.&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
* SQL Transformations&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
=== Semi-dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade option is ideal in most cases, and if not enough, you can use the Cascade Custom option and Overloading Functions.&lt;br /&gt;
* View&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
&lt;br /&gt;
=== Dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade Custom and Overload functions&lt;br /&gt;
* Making your own module, which calls the CustomFields class&lt;br /&gt;
&lt;br /&gt;
=== Timed condition ===&lt;br /&gt;
&lt;br /&gt;
* SQL scheduled events&lt;br /&gt;
* Cron job&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
= Overloading functions =&lt;br /&gt;
&lt;br /&gt;
Here are a few concrete use cases of overloading functions to achieve various goals.&lt;br /&gt;
&lt;br /&gt;
== Dynamic list selection based on another one ==&lt;br /&gt;
&lt;br /&gt;
The goal is to show two custom fields on the Third-Party module: one which gives the zone (secteur) of the third-party (Isere, Alpes du sud, Haute-Savoie...) and the other which gives relative to the zone the list of all the ski resorts (station_a) inside the selected zone.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will show how to manually manage this kind of requirements using [[Module_CustomFields#Overloading_functions:_Adding_your_own_management_code_for_custom_fields|overloading functions]], but in latest CustomFields releases, the same solution can be achieved automatically using the [[Module_CustomFields#Cascade|Cascade option]].&lt;br /&gt;
&lt;br /&gt;
We have one table &#039;&#039;&#039;llx_k_station&#039;&#039;&#039; (rowid, station (char50), secteur(char50)) which contains the following: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1,les 2 alpes,isère&lt;br /&gt;
2,chamrousse,isère&lt;br /&gt;
3,alpe d&#039;huez,isère&lt;br /&gt;
4,vars,alpes du sud&lt;br /&gt;
5,risoul,alpes du sud&lt;br /&gt;
etc...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In CustomFields&#039;s admin panel, we create two custom fields:&lt;br /&gt;
&lt;br /&gt;
1- secteur: returns the list of all zones - type DropdownBox: enum(&#039;- Aucun&#039;,&#039;Alpes du Sud&#039;,&#039;export&#039;,&#039;Haute-Savoie&#039;,&#039;Isère&#039;, etc...)&lt;br /&gt;
&lt;br /&gt;
2- station_a: returns the list of all ski resorts - type constraint on &#039;&#039;&#039;llx_k_station&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Here is the code to put in customfields_fields_extend.lib.php that will allow to show only the ski resorts corresponding to the selected zone:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_editview_societe_station_a (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$parameters, &amp;amp;$action, &amp;amp;$user, &amp;amp;$idvar, &amp;amp;$rightok, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
  global $db; // allow to access database functions&lt;br /&gt;
  $sql=&amp;quot;SELECT llx_k_station.station, llx_k_station.rowid FROM llx_societe_customfields INNER JOIN llx_k_station ON llx_societe_customfields.Secteur = llx_k_station.secteur WHERE llx_societe_customfields.fk_societe=&amp;quot;.$object-&amp;gt;id;&lt;br /&gt;
$result=$db-&amp;gt;query($sql);&lt;br /&gt;
 &lt;br /&gt;
  if ($result) {&lt;br /&gt;
    $num = $db-&amp;gt;num_rows($result);&lt;br /&gt;
    $i = 0;&lt;br /&gt;
    $myarray = array();&lt;br /&gt;
    while ($i &amp;lt; $num)&lt;br /&gt;
    {&lt;br /&gt;
      $obj = $db-&amp;gt;fetch_object($result);&lt;br /&gt;
      $myarray []=array(&#039;id&#039;=&amp;gt;$obj-&amp;gt;rowid, &#039;value&#039;=&amp;gt;$obj-&amp;gt;station);&lt;br /&gt;
      $i++;&lt;br /&gt;
    }&lt;br /&gt;
    $value = $myarray; &lt;br /&gt;
    $db-&amp;gt;free($result);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to manub for giving this case.&lt;br /&gt;
&lt;br /&gt;
== Dynamic discount computation per products lines on propales ==&lt;br /&gt;
&lt;br /&gt;
The goal here is to compute the discount for a product line in propales based on a computation on three custom fields.&lt;br /&gt;
&lt;br /&gt;
First, create three custom fields for &amp;quot;Line commercial proposals&amp;quot;: vl, cpl and lc.&lt;br /&gt;
&lt;br /&gt;
Then add the following overloading function at the end of your customfields_fields_extend.lib.php file (read the comments for more informations):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_propaldet_vl (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
&lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $vl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;vl&#039;};&lt;br /&gt;
    $cpl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;cpl&#039;};&lt;br /&gt;
    $lc = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;lc&#039;};&lt;br /&gt;
&lt;br /&gt;
    // Compute the discount&lt;br /&gt;
    $remise_calculee = (100 - $vl) * (100 - $cpl) * (100 - $lc);&lt;br /&gt;
&lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $remise_calculee = price2num($remise_calculee);&lt;br /&gt;
&lt;br /&gt;
    // Assign the new discount into the PropalLine object&lt;br /&gt;
    $object-&amp;gt;remise_percent = $remise_calculee;&lt;br /&gt;
&lt;br /&gt;
    // Update the price (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
&lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
&lt;br /&gt;
    //$q = &amp;quot;UPDATE  &amp;quot;.MAIN_DB_PREFIX.&amp;quot;propaldet SET remise_percent=&amp;quot;.$remise_calculee.&amp;quot; WHERE  rowid=&amp;quot;.$object-&amp;gt;rowid;&lt;br /&gt;
    //$db-&amp;gt;query($q);&lt;br /&gt;
    &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;vl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;cpl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;lc&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Linking Dolibarr objects from two different modules =&lt;br /&gt;
&lt;br /&gt;
Dolibarr sometimes offers the possibility to link two different modules. Eg: when you convert an Intervention card into an Invoice.&lt;br /&gt;
&lt;br /&gt;
In this case, you might want in your templates to fetch the custom fields of the linked object. Eg: you generate a PDF from the Invoice, but you want to get the custom fields from the Intervention that was converted into the Invoice.&lt;br /&gt;
&lt;br /&gt;
The key here is to use the &#039;&#039;&#039;llx_element_element&#039;&#039;&#039; table or the &#039;&#039;&#039;llx_element_*&#039;&#039;&#039; tables (eg: llx_element_contact): this is a standard Dolibarr table that stores all links between objects of two different modules.&lt;br /&gt;
&lt;br /&gt;
Here is the structure:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
rowid | fk_source | sourcetype | fk_target | targettype&lt;br /&gt;
-------------------------------------------------------&lt;br /&gt;
1 | 2 | fichinter | 5 | facture&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can then use a php code like the following to populate your $object with customfields:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
// This will also create and returns us an instance of the CustomFields class, which we will use to manually query the llx_element_element table&lt;br /&gt;
$customfields = customfields_fill_object($object, null, $outputlangs, &#039;facture&#039;, true); // beautified values&lt;br /&gt;
&lt;br /&gt;
// Querying the llx_element_element table manually to get the link between the invoice and the linked intervention card (we want the id of the intervention card)&lt;br /&gt;
// fetchAny($columns, $table, $where=&#039;&#039;, $orderby=&#039;&#039;, $limitby=&#039;&#039;)&lt;br /&gt;
$eltrow = $customfields-&amp;gt;fetchAny(&#039;*&#039;, MAIN_DB_PREFIX.&#039;element_element&#039;, &#039;targettype=&amp;quot;&#039;.$object-&amp;gt;element.&#039;&amp;quot; and fk_target=&#039;.$object-&amp;gt;id.&#039; and sourcetype=&amp;quot;fichinter&amp;quot;&#039;);&lt;br /&gt;
&lt;br /&gt;
// Save the intervention&#039;s id&lt;br /&gt;
$sourceid = $eltrow[0][&#039;fk_source&#039;];&lt;br /&gt;
&lt;br /&gt;
// Crafting a dummy intervention object&lt;br /&gt;
$fromobject = new stdClass(); // Init an empty object&lt;br /&gt;
$fromobject-&amp;gt;id = $sourceid; // We need the id and..&lt;br /&gt;
$fromobject-&amp;gt;table_element = &#039;fichinter&#039;; // the table_element (name of the table)&lt;br /&gt;
&lt;br /&gt;
// We can then fill $object with the intervention custom fields (we store them in a sub property &#039;fichinter&#039; to avoid conflicts with invoice&#039;s custom fields)&lt;br /&gt;
customfields_fill_object($object, $fromobject, $outputlangs, &#039;fichinter&#039;, true);&lt;br /&gt;
&lt;br /&gt;
// Now you can access invoices custom fields using:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;facture-&amp;gt;cf_myfield);&lt;br /&gt;
// And access intervention custom fields with:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;fichinter-&amp;gt;cf_myfield);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to netassopro for the tip.&lt;br /&gt;
&lt;br /&gt;
= Modify the total price of every product/service line with a coefficient (without tampering the discount) =&lt;br /&gt;
&lt;br /&gt;
Here we will go through the creation and management of a custom field called &#039;&#039;&#039;coefficient&#039;&#039;&#039; that will change the total price of each product by modifying it by the coefficient.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will not create a coefficient per product but rather per product&#039;s line. Thus if you want to create a coefficient that never change between invoices but only depending on the product and stays static, please refer to the next chapter below about creating a custom tax per product.&lt;br /&gt;
&lt;br /&gt;
The preliminary and necessary step is to first create a custom field that will be used to modify the total price. We will create one for Client Invoice Lines.&lt;br /&gt;
&lt;br /&gt;
Go to the CustomFields administration panel, then into the Client Invoice Lines tab, then click on the button New Field, name the field &#039;&#039;&#039;coefficient&#039;&#039;&#039;, and set the type to &#039;&#039;&#039;Double&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
When you go to client invoices in Dolibarr, you should then see something similar to this:&lt;br /&gt;
[[File:Cfe1.jpg]]&lt;br /&gt;
You can see that our coefficient field was successfully created.&lt;br /&gt;
&lt;br /&gt;
Then here are two alternative ways of implementing the change of the total price of each product relativery to this coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change total price in database ==&lt;br /&gt;
&lt;br /&gt;
Here we will use a &amp;quot;save&amp;quot; overloading function to change the total price directly in the database, thus we won&#039;t have to modify anything else (eg: nor PDF templating nor ODT) since the total price will be already updated in the database.&lt;br /&gt;
&lt;br /&gt;
Warning: this method will work only with CustomFields =&amp;gt; 3.2.23 since the &amp;quot;save&amp;quot; overloading function did not work for products lines before.&lt;br /&gt;
&lt;br /&gt;
Just add the following overloading function in your customfields_fields_extend.lib.php file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_facturedet_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
 &lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $coefficient = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;coefficient&#039;}; // you could also use $coefficient = $value;&lt;br /&gt;
 &lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $coefficient = price2num($coefficient);&lt;br /&gt;
 &lt;br /&gt;
    // Get the original subprices (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
 &lt;br /&gt;
    // Update the subprices with the coefficient&lt;br /&gt;
    $object-&amp;gt;total_ht = $object-&amp;gt;total_ht * $coefficient; // compute no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva = $object-&amp;gt;total_tva * $coefficient; // compute tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc * $coefficient; // compute tax included price&lt;br /&gt;
 &lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(&#039;&#039;,true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
 &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;coefficient&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should show the correct total price on the Dolibarr&#039;s interface without modifying the unit subprice (P.U. Unit):&lt;br /&gt;
&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
You have nothing to edit in your PDF or ODT template, since the new subprice with applied coefficient will be automatically printed instead of the old one without coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change printed total price per product without tampering the subprice in the database ==&lt;br /&gt;
&lt;br /&gt;
Here is a way to change the printed total price (the one shown on screen and in PDF/ODT) but without changing the total price in the database.&lt;br /&gt;
&lt;br /&gt;
The following sub-chapters are all independent, thus you can only follow the ones that you require, eg: if you only need to change the price in your PDF documents, just create a custom field then jump to the sub-chapter to &#039;&#039;&#039;Implement the change of price in PDF&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in ODT templates ===&lt;br /&gt;
&lt;br /&gt;
There&#039;s no eval implementation currently inside ODT templates, but there is a kind of hook implementation, called [[Create_an_ODT_document_template#Other_personalized_tags|substitution functions]].&lt;br /&gt;
&lt;br /&gt;
You can either create your own module, then create your own substitution function, but for the sake of simplicity in this tutorial, we will directly use the CustomFields substitution function. However, please remember that you will have to redo your changes everytime you will update the CustomFields module, so if you don&#039;t want to do that it would be better if you create your own module and substitution function.&lt;br /&gt;
&lt;br /&gt;
Open the file &#039;&#039;&#039;htdocs/customfields/core/substitutions/functions_customfields.lib.php&#039;&#039;&#039; and go to the function &#039;&#039;&#039;customfields_completesubstitutionarray_lines&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
At the very end of this function, but inside the customfields check:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) {&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
But just before the ending bracket, you should add the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($object-&amp;gt;table_element_line === &#039;facturedet&#039;) { // First check that we are in the right module, we don&#039;t want to use coefficient for other modules if we didn&#039;t define the custom field there&lt;br /&gt;
    $substitutionarray[&#039;line_price_full&#039;] = $substitutionarray[&#039;line_price_ttc&#039;] * $substitutionarray[&#039;cf_coefficient&#039;]; // multiply the total price with our coefficient&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code will add a new tag &#039;&#039;&#039;{line_price_full}&#039;&#039;&#039; that you can then use inside your ODT templates.&lt;br /&gt;
&lt;br /&gt;
Just place this tag in a table in your ODT document, between the table tags ( [!-- BEGIN row.lines --] ... [!-- END row.lines --] ), and this will print your full price.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in PDF templates ===&lt;br /&gt;
&lt;br /&gt;
First, we will create our own PDF template by copying an existing template, and then we will edit it to change the price of items.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Creating your own PDF template by copying an existing template&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Go into the following folder: &#039;&#039;&#039;htdocs/core/modules/facture/doc/&#039;&#039;&#039;, then copy the file &#039;&#039;&#039;pdf_crabe.modules.php&#039;&#039;&#039; and name the new one &#039;&#039;&#039;pdf_crabecoeff.modules.php&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Then open it in your favourite editor, and change the following lines:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures // change pdf_crabe into pdf_crabecoeff&lt;br /&gt;
{&lt;br /&gt;
...&lt;br /&gt;
    function __construct($db)&lt;br /&gt;
    {&lt;br /&gt;
        ...&lt;br /&gt;
	$this-&amp;gt;name = &amp;quot;crabe&amp;quot;; // change &amp;quot;crabe&amp;quot; into &amp;quot;crabecoeff&amp;quot;&lt;br /&gt;
        ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you have your own PDF template!&lt;br /&gt;
&lt;br /&gt;
Now you should &#039;&#039;&#039;enable your PDF template&#039;&#039;&#039; before doing anything else, by going into the Modules Administration panel, and into the Invoice admin page, then click on the ON button at the right of the &#039;&#039;&#039;crabecoeff&#039;&#039;&#039; entry to enable the template.&lt;br /&gt;
&lt;br /&gt;
Now, let&#039;s proceed onto modifying the prices.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Changing the prices automatically in your PDF&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
You can now do pretty much anything you want in your PDF template, but I will give you a mean to automatically change the prices of every products with our coefficient, as well as the total price, without editing too much the template.&lt;br /&gt;
&lt;br /&gt;
First, try to find the following code of line (should be inside the write_file() method):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf=pdf_getInstance($this-&amp;gt;format);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, just copy/paste the following code &#039;&#039;&#039;below&#039;&#039;&#039; pdf_getInstance():&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$this-&amp;gt;customfields = customfields_fill_object($object, null, $outputlangs, null, true); // beautified values&lt;br /&gt;
$this-&amp;gt;customfields_raw = customfields_fill_object($object, null, $outputlangs, &#039;raw&#039;, null); // raw values&lt;br /&gt;
$this-&amp;gt;customfields_lines = customfields_fill_object_lines($object, null, $outputlangs, null, true); // product lines&#039; values&lt;br /&gt;
&lt;br /&gt;
// Resetting every $object price because we will compute them anew&lt;br /&gt;
$object-&amp;gt;total_ht = 0;&lt;br /&gt;
$object-&amp;gt;total_tva = 0;&lt;br /&gt;
$object-&amp;gt;total_ttc = 0;&lt;br /&gt;
// For each line of product/service, we will recompute all the prices&lt;br /&gt;
foreach($object-&amp;gt;lines as $line) {&lt;br /&gt;
    // Get the coefficient custom field&#039;s value for the current line&lt;br /&gt;
    $coeff = $object-&amp;gt;customfields-&amp;gt;lines-&amp;gt;{$line-&amp;gt;rowid}-&amp;gt;cf_coefficient;&lt;br /&gt;
    // Recompute the price of this product/service line&lt;br /&gt;
    $line-&amp;gt;total_ht = $line-&amp;gt;total_ht*$coeff; // compute no tax price&lt;br /&gt;
    $line-&amp;gt;total_tva = $line-&amp;gt;total_tva*$coeff; // compute tax amount&lt;br /&gt;
    $line-&amp;gt;total_ttc = $line-&amp;gt;total_ttc*$coeff; // compute tax included price&lt;br /&gt;
    // Do the sum of all new products/services prices and tva (will be incremented with every product/service line)&lt;br /&gt;
    $object-&amp;gt;total_ht += $line-&amp;gt;total_ht; // global no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva += $line-&amp;gt;total_tva; // global vat/tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc += $line-&amp;gt;total_ttc; // global  tax included price&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you should try to generate your PDF document, and you should get something like this:&lt;br /&gt;
[[File:Cfe3.jpg]]&lt;br /&gt;
&lt;br /&gt;
Note: printing the coefficient as a column in your PDF document is also possible but will not be covered here because PDF templates will vary for every persons depending on your needs.&lt;br /&gt;
For guidelines about how to do this, you can read the documentation on [[Create_a_PDF_document_template|how to create a PDF template]] and [[Module_CustomFields#Implementing_in_PDF_templates|how to use custom fields in PDF templates]].&lt;br /&gt;
&lt;br /&gt;
Hint: you should be looking inside the following conditional block, inside the write_file() method:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
{&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of total price in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
Unluckily there is not (yet) any hooking context to modify how lines of products/services are printed, thus the only thing you can do is directly edit the &#039;&#039;&#039;template file&#039;&#039;&#039; and manually put inside your code to load your custom field and print it the way you want using HTML formatting, for our example:&lt;br /&gt;
&lt;br /&gt;
* For Dolibarr 3.3 and above, edit the file &#039;&#039;&#039;htdocs/core/tpl/objectline_view.tpl.php&#039;&#039;&#039;&lt;br /&gt;
* For Dolibarr 3.1 and 3.2, edit the file &#039;&#039;&#039;htdocs/core/tpl/freeproductline_view.tpl.php&#039;&#039;&#039; AND &#039;&#039;&#039;predefinedproductline_view.tpl.php&#039;&#039;&#039; (apply the same changes to both files)&lt;br /&gt;
&lt;br /&gt;
In the template file you opened, find the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And replace it by:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $object-&amp;gt;table_element_line; // you can use $object-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
&lt;br /&gt;
// Modifying the HT price and printing it on the Dolibarr&#039;s datasheet interface&lt;br /&gt;
// Applying a multiplying/dividing coefficient to an HT price will give a mathematically correct TTC price since the taxes are also a multiplier&lt;br /&gt;
// However, if you want to apply other mathematical operations (like addition or substaction), you should use the total price with $line-&amp;gt;total_ttc&lt;br /&gt;
if (!empty($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
    echo price($line-&amp;gt;total_ht * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient);&lt;br /&gt;
} else {&lt;br /&gt;
    echo price($line-&amp;gt;total_ht);&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should produce something like this:&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Technical note: here we are using customfields_fill_object() instead of customfields_fill_object_lines() (the latter being specifically adapted to lines and thus should theoretically be used here) for performance reasons because the template file we are editing is called in a loop, and customfields_fill_object_lines() should be called outside a loop (because it already does the job that is done by the loop), so here we &#039;tricks&#039; the customfields_fill_object() function to avoid this loop by submitting &#039;facturedet&#039; as a module by itself (when in fact it&#039;s a product lines table, but it works the same way nevertheless). This is a good demonstration of the flexibility of the CustomFields functions and methods.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the coefficient column in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
It&#039;s also possible to show the coefficient in a new column on the Dolibarr interface.&lt;br /&gt;
&lt;br /&gt;
To do that, you need to do two things:&lt;br /&gt;
&lt;br /&gt;
1- Add the column title &amp;quot;Coeff.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
2- Add the generator that will input the coefficient value for each line.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Add the column title &amp;quot;Coeff.&amp;quot;&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
Open in your favourite editor the file /htdocs/core/class/commonobject.class.php and inside the function printObjectLines (note the plural s), find the following lines of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Total HT&lt;br /&gt;
print &#039;&amp;lt;td align=&amp;quot;right&amp;quot; width=&amp;quot;50&amp;quot;&amp;gt;&#039;.$langs-&amp;gt;trans(&#039;TotalHTShort&#039;).&#039;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is what prints the Total HT price column. We will place the Coeff column just before.&lt;br /&gt;
&lt;br /&gt;
So, just above this code, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
dol_include_once(&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element_line);&lt;br /&gt;
$coeff_field = $customfields-&amp;gt;fetchFieldStruct(&#039;coefficient&#039;);&lt;br /&gt;
if (!empty($coeff_field)) {&lt;br /&gt;
    print &#039;&amp;lt;td align=&amp;quot;right&amp;quot; width=&amp;quot;50&amp;quot;&amp;gt;&#039;.$langs-&amp;gt;trans(&#039;Coeff.&#039;).&#039;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will take care of showing the Coeff. column, but only for modules where you created such a custom field (because the file we modified is a common object for all modules in Dolibarr, not just invoices, thus this modification will gracefully skip this custom field if it does not exists for other modules).&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Add the generator&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/tpl/objectline_view.tpl.php and try to find the following block of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php if ($line-&amp;gt;special_code == 3)	{ ?&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo $langs-&amp;gt;trans(&#039;Option&#039;); ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } else { ?&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is what prints the total HT price value for each line.&lt;br /&gt;
&lt;br /&gt;
Just above of this block of code, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $object-&amp;gt;table_element_line; // you can use $this-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
if (isset($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
    &amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient; ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just like for the column title, this code will take care of generating the coefficient value only if the custom field is defined, because this file is also a generic template for all modules of Dolibarr.&lt;br /&gt;
&lt;br /&gt;
At this point, everything is done, you should see the following:&lt;br /&gt;
[[File:Cf_wiki_coeff_column.png]]&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in margin module ===&lt;br /&gt;
&lt;br /&gt;
If you enabled the margin module, you should see that the coefficient is not applied there, but it&#039;s easy to fix that.&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/class/commonobject.class.php and find the function getMarginInfos(), then inside find the following lines:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach($this-&amp;gt;lines as $line) {&lt;br /&gt;
    if (empty($line-&amp;gt;pa_ht) &amp;amp;&amp;amp; isset($line-&amp;gt;fk_fournprice) &amp;amp;&amp;amp; !$force_price) {&lt;br /&gt;
        $product = new ProductFournisseur($this-&amp;gt;db);&lt;br /&gt;
        if ($product-&amp;gt;fetch_product_fournisseur_price($line-&amp;gt;fk_fournprice))&lt;br /&gt;
            $line-&amp;gt;pa_ht = $product-&amp;gt;fourn_unitprice * (1 - $product-&amp;gt;fourn_remise_percent / 100);&lt;br /&gt;
        if (isset($conf-&amp;gt;global-&amp;gt;MARGIN_TYPE) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;MARGIN_TYPE == &amp;quot;2&amp;quot; &amp;amp;&amp;amp; $product-&amp;gt;fourn_unitcharges &amp;gt; 0)&lt;br /&gt;
            $line-&amp;gt;pa_ht += $product-&amp;gt;fourn_unitcharges;&lt;br /&gt;
    }&lt;br /&gt;
    // si prix d&#039;achat non renseigné et devrait l&#039;être, alors prix achat = prix vente&lt;br /&gt;
    if ((!isset($line-&amp;gt;pa_ht) || $line-&amp;gt;pa_ht == 0) &amp;amp;&amp;amp; $line-&amp;gt;subprice &amp;gt; 0 &amp;amp;&amp;amp; (isset($conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull == 1)) {&lt;br /&gt;
        $line-&amp;gt;pa_ht = $line-&amp;gt;subprice * (1 - ($line-&amp;gt;remise_percent / 100));&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the lines are filled with margin infos. The price has just been fetched here, and we will now modify it with the coefficient before the price gets used for margin computation.&lt;br /&gt;
&lt;br /&gt;
Just below the code we found, paste the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $line-&amp;gt;table_element;&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
if (!empty($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
    $line-&amp;gt;pa_ht = $line-&amp;gt;pa_ht * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient;&lt;br /&gt;
    $line-&amp;gt;subprice = $line-&amp;gt;subprice * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s all, the price should be reflected in the margin.&lt;br /&gt;
[[File:Cf_wiki_coeff_margin.png]]&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in remaining debt to pay ===&lt;br /&gt;
&lt;br /&gt;
Open /htdocs/core/class/commonobject.class.php and look for the function update_price().&lt;br /&gt;
&lt;br /&gt;
You should see the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function update_price($exclspec=0,$roundingadjust=&#039;none&#039;,$nodatabaseupdate=0,$seller=&#039;&#039;)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just below the include_once line, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;;&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/conf/conf_customfields_func.lib.php&#039;;&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $this-&amp;gt;table_element_line);&lt;br /&gt;
// Fetching line ids from parent object id&lt;br /&gt;
$line_ids = $customfields-&amp;gt;fetchAny(&#039;rowid&#039;, MAIN_DB_PREFIX.$this-&amp;gt;table_element_line, $this-&amp;gt;fk_element.&#039; = &#039;.$this-&amp;gt;id);&lt;br /&gt;
$line_ids = array_values_recursive(&#039;rowid&#039;, $line_ids);&lt;br /&gt;
// Load custom fields&lt;br /&gt;
$cf_records = $customfields-&amp;gt;fetch($line_ids);&lt;br /&gt;
// Reassociate the records with the line id (instead of the customfields record id)&lt;br /&gt;
$cf_line_cfrowid_to_rowid = array_reassociate(&#039;rowid&#039;, &#039;fk_&#039;.$this-&amp;gt;table_element_line, $cf_records);&lt;br /&gt;
$tmp = $cf_records;&lt;br /&gt;
$cf_records = array();&lt;br /&gt;
foreach ($tmp as $k=&amp;gt;$v) { $cf_records[$cf_line_cfrowid_to_rowid[$k]] = $tmp[$k]; }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will load the coefficient custom field (and also any other custom field you have defined for lines).&lt;br /&gt;
&lt;br /&gt;
Now scroll below, and you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
while ($i &amp;lt; $num)&lt;br /&gt;
{&lt;br /&gt;
    $obj = $this-&amp;gt;db-&amp;gt;fetch_object($resql);&lt;br /&gt;
&lt;br /&gt;
    // Note: There is no check on detail line and no check on total, if $forcedroundingmode = &#039;none&#039;&lt;br /&gt;
&lt;br /&gt;
    if ($forcedroundingmode == &#039;0&#039;)	// Check if data on line are consistent. This may solve lines that were not consistent because set with $forcedroundingmode=&#039;auto&#039;&lt;br /&gt;
    {&lt;br /&gt;
        $localtax_array=array($obj-&amp;gt;localtax1_type,$obj-&amp;gt;localtax1_tx,$obj-&amp;gt;localtax2_type,$obj-&amp;gt;localtax2_tx);&lt;br /&gt;
        $tmpcal=calcul_price_total($obj-&amp;gt;qty, $obj-&amp;gt;up, $obj-&amp;gt;remise_percent, $obj-&amp;gt;vatrate, $obj-&amp;gt;localtax1_tx, $obj-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $obj-&amp;gt;info_bits, $obj-&amp;gt;product_type, $seller, $localtax_array);&lt;br /&gt;
        $diff=price2num($tmpcal[1] - $obj-&amp;gt;total_tva, &#039;MT&#039;, 1);&lt;br /&gt;
        if ($diff)&lt;br /&gt;
        {&lt;br /&gt;
            $sqlfix=&amp;quot;UPDATE &amp;quot;.MAIN_DB_PREFIX.$this-&amp;gt;table_element_line.&amp;quot; SET &amp;quot;.$fieldtva.&amp;quot; = &amp;quot;.$tmpcal[1].&amp;quot;, total_ttc = &amp;quot;.$tmpcal[2].&amp;quot; WHERE rowid = &amp;quot;.$obj-&amp;gt;rowid;&lt;br /&gt;
            dol_syslog(&#039;We found unconsistent data into detailed line (difference of &#039;.$diff.&#039;) for line rowid = &#039;.$obj-&amp;gt;rowid.&amp;quot; (total vat of line calculated=&amp;quot;.$tmpcal[1].&amp;quot;, database=&amp;quot;.$obj-&amp;gt;total_tva.&amp;quot;). We fix the total_vat and total_ttc of line by running sqlfix = &amp;quot;.$sqlfix);&lt;br /&gt;
            $resqlfix=$this-&amp;gt;db-&amp;gt;query($sqlfix);&lt;br /&gt;
            if (! $resqlfix) dol_print_error($this-&amp;gt;db,&#039;Failed to update line&#039;);&lt;br /&gt;
            $obj-&amp;gt;total_tva = $tmpcal[1];&lt;br /&gt;
            $obj-&amp;gt;total_ttc = $tmpcal[2];&lt;br /&gt;
            //&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just after, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
$cf_coeff = $cf_records[$obj-&amp;gt;rowid]-&amp;gt;coefficient;&lt;br /&gt;
if (empty($cf_coeff) and $cf_coeff !== 0) $cf_coeff = 1; // by default, an empty coefficient = 1&lt;br /&gt;
$obj-&amp;gt;total_ht = $obj-&amp;gt;total_ht * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_tva = $obj-&amp;gt;total_tva * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_localtax1 = $obj-&amp;gt;total_localtax1 * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_localtax2 = $obj-&amp;gt;total_localtax2 * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_ttc = $obj-&amp;gt;total_ttc * $cf_coeff;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should do the trick. Now go to one of your invoices (or whatever module you are targetting), and you should see this:&lt;br /&gt;
[[File:Cf_wiki_coeff_remaining_debt.png.png]]&lt;br /&gt;
&lt;br /&gt;
= Custom tax per product =&lt;br /&gt;
&lt;br /&gt;
This part will show you how to create a static custom tax per product. Static means that the tax will be stored per product, and not per product&#039;s line, thus you just have to setup the tax once per product and it will always be applied in every invoice you add this product.&lt;br /&gt;
&lt;br /&gt;
To do that, you will need the CustomFields Pro module + ProductsEasyAccess.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
1- Create a product&#039;s custom fields called &amp;quot;custom_tax&amp;quot;. Now, using ProductsEasyAccess, you can access this field wherever you wants, either to show it on the Dolibarr&#039;s interface (see previous chapter), or in a PDF template, which we will describe below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2- Modify your invoice&#039;s PDF template. You first need to load all the products&#039; fields &#039;&#039;before&#039;&#039; the loop over the products&#039; lines. To do this, search for a line like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// This should be placed above the &amp;quot;loop lines&amp;quot; below, add this:&lt;br /&gt;
&lt;br /&gt;
dol_include_once(&#039;/productseasyaccess/lib/productseasyaccess.lib.php&#039;);&lt;br /&gt;
fill_object_with_products_fields($object);&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines, search for this command&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  // Add this just below the for loop, this will ease your php manipulation after.&lt;br /&gt;
  // This will allow you to more easily access the products&#039; fields (this will store the id of each product inside $lineproductid).&lt;br /&gt;
  $line_product_id = $object-&amp;gt;lines[$i]-&amp;gt;fk_product;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4- You can now use your products&#039; fields in your PDF. To access the custom_tax field, just use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And to print it in your PDF:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell(0,3, $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax, 0, &#039;L&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5- Then, if you want to count the custom tax total and add it to your vat total, you can create a counter inside the for loop we cited in the first step:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_custom_tax = 0; // Initialize the custom tax total counter&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  $object-&amp;gt;total_custom_tax += $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then find inside the function **_tableau_tot**, find the following line:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And prepend the total modification like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc + $object-&amp;gt;total_custom_tax; // prepend this to add the custom tax to the total&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Done. This should show your custom tax per product and your total including all custom taxes.&lt;/div&gt;</summary>
		<author><name>Lrq3000</name></author>
	</entry>
	<entry>
		<id>https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33048</id>
		<title>Module CustomFields Cases</title>
		<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33048"/>
		<updated>2015-01-07T18:59:10Z</updated>

		<summary type="html">&lt;p&gt;Lrq3000: /* Change printed total price per product without tampering the subprice in the database */ Add implementation in remaining debt to pay + images&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:CustomFields]]&lt;br /&gt;
{{TemplateDocDevEn}}&lt;br /&gt;
{{TemplateModEN}}&lt;br /&gt;
{{BasculeDevUserEn|&lt;br /&gt;
name=CustomFields|&lt;br /&gt;
num=8500|&lt;br /&gt;
devdoc=[[Module_CustomFields_Dev]]|&lt;br /&gt;
userdoc=[[Module_CustomFields]]|}}&lt;br /&gt;
This page lists a compilation of concrete, practical examples uses of [[Module_CustomFields|CustomFields]] and of its various features.&lt;br /&gt;
&lt;br /&gt;
If you had an experience with CustomFields, you can also add here your case as an example for others to get inspired.&lt;br /&gt;
&lt;br /&gt;
= How to make a generic PDF template listing all custom fields =&lt;br /&gt;
&lt;br /&gt;
To make a generic PDF template listing all custom fields, you just need to take an existing PDF template, then you need to follow these 3 steps:&lt;br /&gt;
&lt;br /&gt;
1/ Edit the class name (required, else it will conflict with the original template).&lt;br /&gt;
&lt;br /&gt;
Eg: if we take the crabe template for invoices, we change:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;crabe&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
into:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_customfields extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;customfields&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2/ Copy this generic function at the end of the PDF:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propale/order/etc... (CustomFields simpler functions will automatically adapt)&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs); // set default PDF font size&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
    // Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
    $customfields = customfields_fill_object($object, null, $outputlangs, null, true);&lt;br /&gt;
&lt;br /&gt;
    // Setting the starting position of the text cursor&lt;br /&gt;
    $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
    $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
    // Printing the customfields&lt;br /&gt;
    foreach ($object-&amp;gt;customfields as $label=&amp;gt;$value) { // $value is already formatted!&lt;br /&gt;
        // Get translated label&lt;br /&gt;
        $translatedlabel = $customfields-&amp;gt;findLabelPDF($label, $outputlangs); // translated label of the customfield (not translated by default in customfields_fill_object() because a field should always be accessible by a base name, whatever the translation is)&lt;br /&gt;
&lt;br /&gt;
        // PDF formatting, placement and printing&lt;br /&gt;
        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
        $pdf-&amp;gt;MultiCell(0,3, $translatedlabel.&#039; ($object-&amp;gt;customfields-&amp;gt;&#039;.$label.&#039;): &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternative function (with a bit less functionalities, but more controllable):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page (used to debug if CustomFields setup is correct)&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propal/product/whatever...&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs);&lt;br /&gt;
&lt;br /&gt;
            if (empty($object-&amp;gt;table_element) or empty($object-&amp;gt;id)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;Current \$object is not compatible with CustomFields, could not find table_element or id.&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
    $customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element);&lt;br /&gt;
&lt;br /&gt;
            // Fetching custom fields records&lt;br /&gt;
            $fields = $customfields-&amp;gt;fetch($object-&amp;gt;id);&lt;br /&gt;
&lt;br /&gt;
            if (!isset($fields)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;No custom field could be found for this object. Please check your configuration (did you create at least one customfield and set a value in the current datasheet?)&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            } else {&lt;br /&gt;
                // Setting the starting position of the text cursor&lt;br /&gt;
                $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
                $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
                // Printing the customfields&lt;br /&gt;
                foreach ($fields as $key=&amp;gt;$field) {&lt;br /&gt;
                        $translatedname = $customfields-&amp;gt;findLabelPDF($key, $outputlangs); // label of the customfield&lt;br /&gt;
                        $value = $customfields-&amp;gt;simpleprintFieldPDF($key, $field, $outputlangs); // value (cleaned and properly formatted) of the customfield&lt;br /&gt;
&lt;br /&gt;
                        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
                        $pdf-&amp;gt;MultiCell(0,3, $translatedname.&#039;: &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
                        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3/ Call the _pagecustomfields function at the right place (generally, after _pagefoot and just before $pdf-&amp;gt;Close()):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Pied de page&lt;br /&gt;
$this-&amp;gt;_pagefoot($pdf,$object,$outputlangs);&lt;br /&gt;
$pdf-&amp;gt;AliasNbPages();&lt;br /&gt;
&lt;br /&gt;
// CustomFields&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) { // if the customfields module is activated...&lt;br /&gt;
        // Add a page with the customfields&lt;br /&gt;
        $pdf-&amp;gt;AddPage();&lt;br /&gt;
        $this-&amp;gt;_pagecustomfields($pdf,$object,$outputlangs);&lt;br /&gt;
        $pagenb++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
$pdf-&amp;gt;Close(); // you should first search for this, you should only find one occurrence&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then go to the module&#039;s admin panel (eg: invoice admin panel) and enable the template you&#039;ve just created.&lt;br /&gt;
&lt;br /&gt;
You can now use it to test if your customfields work.&lt;br /&gt;
&lt;br /&gt;
= Conditions on fields selection =&lt;br /&gt;
&lt;br /&gt;
Sometimes you may want to show a DropdownBox or Constraint (or another multi-choice type), but you may want to show or hide choices based on conditions (eg: only if it&#039;s a prospect, only if this zone was selected, only if this category was selected, etc..).&lt;br /&gt;
&lt;br /&gt;
To know which is the best solution you should choose to implement your condition, you should first know in which category your condition falls in.&lt;br /&gt;
&lt;br /&gt;
== Conditions categories ==&lt;br /&gt;
&lt;br /&gt;
Conditions can be categorized into 4 types:&lt;br /&gt;
&lt;br /&gt;
* static condition: choices are restrained on a condition that never changes (eg: only show third-parties that are suppliers, or lowercase all data on sql insertion, or check that a field is above a certain number, etc.).&lt;br /&gt;
* semi-dynamic condition: choices that are constrained on a static table but based on the value of another field/customfield. This is also commonly called &amp;quot;Cascaded dropdown lists&amp;quot; or &amp;quot;Dynamically linked dropdown lists&amp;quot;.&lt;br /&gt;
* dynamic: everything is dynamic: choices are based on a subfield of a field of the current module, so that there&#039;s no material table and it&#039;s impossible to create a view (because you would need to create one view per id) (eg: show only the contacts attached to the third-party being accessed)&lt;br /&gt;
* timed condition: a condition that is based on time or recurrence (eg: at 5:00 AM delete old records, etc..)&lt;br /&gt;
&lt;br /&gt;
== Conditions solutions ==&lt;br /&gt;
&lt;br /&gt;
Now that you know what kind of condition you want to set, you can choose one of the following solutions depending on your preferences and knowledge.&lt;br /&gt;
&lt;br /&gt;
In any case, you can always use the &#039;&#039;&#039;CustomFields&#039;s overload functions&#039;&#039;&#039; to fit your needs for nearly every type of condition.&lt;br /&gt;
&lt;br /&gt;
=== Static condition ===&lt;br /&gt;
&lt;br /&gt;
* View: create a view on the table based on the condition, and then create a custom field constrained on this view (which is just like any table), eg: only show third-parties that are suppliers:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
CREATE VIEW llx_societe_supplier_view AS&lt;br /&gt;
SELECT *&lt;br /&gt;
FROM llx_societe&lt;br /&gt;
WHERE fournisseur=1;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
WARNING: foreign keys (and thus constraints) can&#039;t be used on views with MySQL, and materialized views neither exist, so you can&#039;t use this method with MySQL.&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
* SQL Transformations&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
=== Semi-dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade option is ideal in most cases, and if not enough, you can use the Cascade Custom option and Overloading Functions.&lt;br /&gt;
* View&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
&lt;br /&gt;
=== Dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade Custom and Overload functions&lt;br /&gt;
* Making your own module, which calls the CustomFields class&lt;br /&gt;
&lt;br /&gt;
=== Timed condition ===&lt;br /&gt;
&lt;br /&gt;
* SQL scheduled events&lt;br /&gt;
* Cron job&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
= Overloading functions =&lt;br /&gt;
&lt;br /&gt;
Here are a few concrete use cases of overloading functions to achieve various goals.&lt;br /&gt;
&lt;br /&gt;
== Dynamic list selection based on another one ==&lt;br /&gt;
&lt;br /&gt;
The goal is to show two custom fields on the Third-Party module: one which gives the zone (secteur) of the third-party (Isere, Alpes du sud, Haute-Savoie...) and the other which gives relative to the zone the list of all the ski resorts (station_a) inside the selected zone.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will show how to manually manage this kind of requirements using [[Module_CustomFields#Overloading_functions:_Adding_your_own_management_code_for_custom_fields|overloading functions]], but in latest CustomFields releases, the same solution can be achieved automatically using the [[Module_CustomFields#Cascade|Cascade option]].&lt;br /&gt;
&lt;br /&gt;
We have one table &#039;&#039;&#039;llx_k_station&#039;&#039;&#039; (rowid, station (char50), secteur(char50)) which contains the following: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1,les 2 alpes,isère&lt;br /&gt;
2,chamrousse,isère&lt;br /&gt;
3,alpe d&#039;huez,isère&lt;br /&gt;
4,vars,alpes du sud&lt;br /&gt;
5,risoul,alpes du sud&lt;br /&gt;
etc...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In CustomFields&#039;s admin panel, we create two custom fields:&lt;br /&gt;
&lt;br /&gt;
1- secteur: returns the list of all zones - type DropdownBox: enum(&#039;- Aucun&#039;,&#039;Alpes du Sud&#039;,&#039;export&#039;,&#039;Haute-Savoie&#039;,&#039;Isère&#039;, etc...)&lt;br /&gt;
&lt;br /&gt;
2- station_a: returns the list of all ski resorts - type constraint on &#039;&#039;&#039;llx_k_station&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Here is the code to put in customfields_fields_extend.lib.php that will allow to show only the ski resorts corresponding to the selected zone:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_editview_societe_station_a (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$parameters, &amp;amp;$action, &amp;amp;$user, &amp;amp;$idvar, &amp;amp;$rightok, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
  global $db; // allow to access database functions&lt;br /&gt;
  $sql=&amp;quot;SELECT llx_k_station.station, llx_k_station.rowid FROM llx_societe_customfields INNER JOIN llx_k_station ON llx_societe_customfields.Secteur = llx_k_station.secteur WHERE llx_societe_customfields.fk_societe=&amp;quot;.$object-&amp;gt;id;&lt;br /&gt;
$result=$db-&amp;gt;query($sql);&lt;br /&gt;
 &lt;br /&gt;
  if ($result) {&lt;br /&gt;
    $num = $db-&amp;gt;num_rows($result);&lt;br /&gt;
    $i = 0;&lt;br /&gt;
    $myarray = array();&lt;br /&gt;
    while ($i &amp;lt; $num)&lt;br /&gt;
    {&lt;br /&gt;
      $obj = $db-&amp;gt;fetch_object($result);&lt;br /&gt;
      $myarray []=array(&#039;id&#039;=&amp;gt;$obj-&amp;gt;rowid, &#039;value&#039;=&amp;gt;$obj-&amp;gt;station);&lt;br /&gt;
      $i++;&lt;br /&gt;
    }&lt;br /&gt;
    $value = $myarray; &lt;br /&gt;
    $db-&amp;gt;free($result);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to manub for giving this case.&lt;br /&gt;
&lt;br /&gt;
== Dynamic discount computation per products lines on propales ==&lt;br /&gt;
&lt;br /&gt;
The goal here is to compute the discount for a product line in propales based on a computation on three custom fields.&lt;br /&gt;
&lt;br /&gt;
First, create three custom fields for &amp;quot;Line commercial proposals&amp;quot;: vl, cpl and lc.&lt;br /&gt;
&lt;br /&gt;
Then add the following overloading function at the end of your customfields_fields_extend.lib.php file (read the comments for more informations):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_propaldet_vl (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
&lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $vl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;vl&#039;};&lt;br /&gt;
    $cpl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;cpl&#039;};&lt;br /&gt;
    $lc = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;lc&#039;};&lt;br /&gt;
&lt;br /&gt;
    // Compute the discount&lt;br /&gt;
    $remise_calculee = (100 - $vl) * (100 - $cpl) * (100 - $lc);&lt;br /&gt;
&lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $remise_calculee = price2num($remise_calculee);&lt;br /&gt;
&lt;br /&gt;
    // Assign the new discount into the PropalLine object&lt;br /&gt;
    $object-&amp;gt;remise_percent = $remise_calculee;&lt;br /&gt;
&lt;br /&gt;
    // Update the price (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
&lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
&lt;br /&gt;
    //$q = &amp;quot;UPDATE  &amp;quot;.MAIN_DB_PREFIX.&amp;quot;propaldet SET remise_percent=&amp;quot;.$remise_calculee.&amp;quot; WHERE  rowid=&amp;quot;.$object-&amp;gt;rowid;&lt;br /&gt;
    //$db-&amp;gt;query($q);&lt;br /&gt;
    &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;vl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;cpl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;lc&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Linking Dolibarr objects from two different modules =&lt;br /&gt;
&lt;br /&gt;
Dolibarr sometimes offers the possibility to link two different modules. Eg: when you convert an Intervention card into an Invoice.&lt;br /&gt;
&lt;br /&gt;
In this case, you might want in your templates to fetch the custom fields of the linked object. Eg: you generate a PDF from the Invoice, but you want to get the custom fields from the Intervention that was converted into the Invoice.&lt;br /&gt;
&lt;br /&gt;
The key here is to use the &#039;&#039;&#039;llx_element_element&#039;&#039;&#039; table or the &#039;&#039;&#039;llx_element_*&#039;&#039;&#039; tables (eg: llx_element_contact): this is a standard Dolibarr table that stores all links between objects of two different modules.&lt;br /&gt;
&lt;br /&gt;
Here is the structure:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
rowid | fk_source | sourcetype | fk_target | targettype&lt;br /&gt;
-------------------------------------------------------&lt;br /&gt;
1 | 2 | fichinter | 5 | facture&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can then use a php code like the following to populate your $object with customfields:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
// This will also create and returns us an instance of the CustomFields class, which we will use to manually query the llx_element_element table&lt;br /&gt;
$customfields = customfields_fill_object($object, null, $outputlangs, &#039;facture&#039;, true); // beautified values&lt;br /&gt;
&lt;br /&gt;
// Querying the llx_element_element table manually to get the link between the invoice and the linked intervention card (we want the id of the intervention card)&lt;br /&gt;
// fetchAny($columns, $table, $where=&#039;&#039;, $orderby=&#039;&#039;, $limitby=&#039;&#039;)&lt;br /&gt;
$eltrow = $customfields-&amp;gt;fetchAny(&#039;*&#039;, MAIN_DB_PREFIX.&#039;element_element&#039;, &#039;targettype=&amp;quot;&#039;.$object-&amp;gt;element.&#039;&amp;quot; and fk_target=&#039;.$object-&amp;gt;id.&#039; and sourcetype=&amp;quot;fichinter&amp;quot;&#039;);&lt;br /&gt;
&lt;br /&gt;
// Save the intervention&#039;s id&lt;br /&gt;
$sourceid = $eltrow[0][&#039;fk_source&#039;];&lt;br /&gt;
&lt;br /&gt;
// Crafting a dummy intervention object&lt;br /&gt;
$fromobject = new stdClass(); // Init an empty object&lt;br /&gt;
$fromobject-&amp;gt;id = $sourceid; // We need the id and..&lt;br /&gt;
$fromobject-&amp;gt;table_element = &#039;fichinter&#039;; // the table_element (name of the table)&lt;br /&gt;
&lt;br /&gt;
// We can then fill $object with the intervention custom fields (we store them in a sub property &#039;fichinter&#039; to avoid conflicts with invoice&#039;s custom fields)&lt;br /&gt;
customfields_fill_object($object, $fromobject, $outputlangs, &#039;fichinter&#039;, true);&lt;br /&gt;
&lt;br /&gt;
// Now you can access invoices custom fields using:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;facture-&amp;gt;cf_myfield);&lt;br /&gt;
// And access intervention custom fields with:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;fichinter-&amp;gt;cf_myfield);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to netassopro for the tip.&lt;br /&gt;
&lt;br /&gt;
= Modify the total price of every product/service line with a coefficient (without tampering the discount) =&lt;br /&gt;
&lt;br /&gt;
Here we will go through the creation and management of a custom field called &#039;&#039;&#039;coefficient&#039;&#039;&#039; that will change the total price of each product by modifying it by the coefficient.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will not create a coefficient per product but rather per product&#039;s line. Thus if you want to create a coefficient that never change between invoices but only depending on the product and stays static, please refer to the next chapter below about creating a custom tax per product.&lt;br /&gt;
&lt;br /&gt;
The preliminary and necessary step is to first create a custom field that will be used to modify the total price. We will create one for Client Invoice Lines.&lt;br /&gt;
&lt;br /&gt;
Go to the CustomFields administration panel, then into the Client Invoice Lines tab, then click on the button New Field, name the field &#039;&#039;&#039;coefficient&#039;&#039;&#039;, and set the type to &#039;&#039;&#039;Double&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
When you go to client invoices in Dolibarr, you should then see something similar to this:&lt;br /&gt;
[[File:Cfe1.jpg]]&lt;br /&gt;
You can see that our coefficient field was successfully created.&lt;br /&gt;
&lt;br /&gt;
Then here are two alternative ways of implementing the change of the total price of each product relativery to this coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change total price in database ==&lt;br /&gt;
&lt;br /&gt;
Here we will use a &amp;quot;save&amp;quot; overloading function to change the total price directly in the database, thus we won&#039;t have to modify anything else (eg: nor PDF templating nor ODT) since the total price will be already updated in the database.&lt;br /&gt;
&lt;br /&gt;
Warning: this method will work only with CustomFields =&amp;gt; 3.2.23 since the &amp;quot;save&amp;quot; overloading function did not work for products lines before.&lt;br /&gt;
&lt;br /&gt;
Just add the following overloading function in your customfields_fields_extend.lib.php file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_facturedet_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
 &lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $coefficient = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;coefficient&#039;}; // you could also use $coefficient = $value;&lt;br /&gt;
 &lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $coefficient = price2num($coefficient);&lt;br /&gt;
 &lt;br /&gt;
    // Get the original subprices (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
 &lt;br /&gt;
    // Update the subprices with the coefficient&lt;br /&gt;
    $object-&amp;gt;total_ht = $object-&amp;gt;total_ht * $coefficient; // compute no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva = $object-&amp;gt;total_tva * $coefficient; // compute tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc * $coefficient; // compute tax included price&lt;br /&gt;
 &lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(&#039;&#039;,true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
 &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;coefficient&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should show the correct total price on the Dolibarr&#039;s interface without modifying the unit subprice (P.U. Unit):&lt;br /&gt;
&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
You have nothing to edit in your PDF or ODT template, since the new subprice with applied coefficient will be automatically printed instead of the old one without coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change printed total price per product without tampering the subprice in the database ==&lt;br /&gt;
&lt;br /&gt;
Here is a way to change the printed total price (the one shown on screen and in PDF/ODT) but without changing the total price in the database.&lt;br /&gt;
&lt;br /&gt;
The following sub-chapters are all independent, thus you can only follow the ones that you require, eg: if you only need to change the price in your PDF documents, just create a custom field then jump to the sub-chapter to &#039;&#039;&#039;Implement the change of price in PDF&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in ODT templates ===&lt;br /&gt;
&lt;br /&gt;
There&#039;s no eval implementation currently inside ODT templates, but there is a kind of hook implementation, called [[Create_an_ODT_document_template#Other_personalized_tags|substitution functions]].&lt;br /&gt;
&lt;br /&gt;
You can either create your own module, then create your own substitution function, but for the sake of simplicity in this tutorial, we will directly use the CustomFields substitution function. However, please remember that you will have to redo your changes everytime you will update the CustomFields module, so if you don&#039;t want to do that it would be better if you create your own module and substitution function.&lt;br /&gt;
&lt;br /&gt;
Open the file &#039;&#039;&#039;htdocs/customfields/core/substitutions/functions_customfields.lib.php&#039;&#039;&#039; and go to the function &#039;&#039;&#039;customfields_completesubstitutionarray_lines&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
At the very end of this function, but inside the customfields check:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) {&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
But just before the ending bracket, you should add the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($object-&amp;gt;table_element_line === &#039;facturedet&#039;) { // First check that we are in the right module, we don&#039;t want to use coefficient for other modules if we didn&#039;t define the custom field there&lt;br /&gt;
    $substitutionarray[&#039;line_price_full&#039;] = $substitutionarray[&#039;line_price_ttc&#039;] * $substitutionarray[&#039;cf_coefficient&#039;]; // multiply the total price with our coefficient&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code will add a new tag &#039;&#039;&#039;{line_price_full}&#039;&#039;&#039; that you can then use inside your ODT templates.&lt;br /&gt;
&lt;br /&gt;
Just place this tag in a table in your ODT document, between the table tags ( [!-- BEGIN row.lines --] ... [!-- END row.lines --] ), and this will print your full price.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in PDF templates ===&lt;br /&gt;
&lt;br /&gt;
First, we will create our own PDF template by copying an existing template, and then we will edit it to change the price of items.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Creating your own PDF template by copying an existing template&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Go into the following folder: &#039;&#039;&#039;htdocs/core/modules/facture/doc/&#039;&#039;&#039;, then copy the file &#039;&#039;&#039;pdf_crabe.modules.php&#039;&#039;&#039; and name the new one &#039;&#039;&#039;pdf_crabecoeff.modules.php&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Then open it in your favourite editor, and change the following lines:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures // change pdf_crabe into pdf_crabecoeff&lt;br /&gt;
{&lt;br /&gt;
...&lt;br /&gt;
    function __construct($db)&lt;br /&gt;
    {&lt;br /&gt;
        ...&lt;br /&gt;
	$this-&amp;gt;name = &amp;quot;crabe&amp;quot;; // change &amp;quot;crabe&amp;quot; into &amp;quot;crabecoeff&amp;quot;&lt;br /&gt;
        ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you have your own PDF template!&lt;br /&gt;
&lt;br /&gt;
Now you should &#039;&#039;&#039;enable your PDF template&#039;&#039;&#039; before doing anything else, by going into the Modules Administration panel, and into the Invoice admin page, then click on the ON button at the right of the &#039;&#039;&#039;crabecoeff&#039;&#039;&#039; entry to enable the template.&lt;br /&gt;
&lt;br /&gt;
Now, let&#039;s proceed onto modifying the prices.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Changing the prices automatically in your PDF&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
You can now do pretty much anything you want in your PDF template, but I will give you a mean to automatically change the prices of every products with our coefficient, as well as the total price, without editing too much the template.&lt;br /&gt;
&lt;br /&gt;
First, try to find the following code of line (should be inside the write_file() method):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf=pdf_getInstance($this-&amp;gt;format);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, just copy/paste the following code &#039;&#039;&#039;below&#039;&#039;&#039; pdf_getInstance():&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$this-&amp;gt;customfields = customfields_fill_object($object, null, $outputlangs, null, true); // beautified values&lt;br /&gt;
$this-&amp;gt;customfields_raw = customfields_fill_object($object, null, $outputlangs, &#039;raw&#039;, null); // raw values&lt;br /&gt;
$this-&amp;gt;customfields_lines = customfields_fill_object_lines($object, null, $outputlangs, null, true); // product lines&#039; values&lt;br /&gt;
&lt;br /&gt;
// Resetting every $object price because we will compute them anew&lt;br /&gt;
$object-&amp;gt;total_ht = 0;&lt;br /&gt;
$object-&amp;gt;total_tva = 0;&lt;br /&gt;
$object-&amp;gt;total_ttc = 0;&lt;br /&gt;
// For each line of product/service, we will recompute all the prices&lt;br /&gt;
foreach($object-&amp;gt;lines as $line) {&lt;br /&gt;
    // Get the coefficient custom field&#039;s value for the current line&lt;br /&gt;
    $coeff = $object-&amp;gt;customfields-&amp;gt;lines-&amp;gt;{$line-&amp;gt;rowid}-&amp;gt;cf_coefficient;&lt;br /&gt;
    // Recompute the price of this product/service line&lt;br /&gt;
    $line-&amp;gt;total_ht = $line-&amp;gt;total_ht*$coeff; // compute no tax price&lt;br /&gt;
    $line-&amp;gt;total_tva = $line-&amp;gt;total_tva*$coeff; // compute tax amount&lt;br /&gt;
    $line-&amp;gt;total_ttc = $line-&amp;gt;total_ttc*$coeff; // compute tax included price&lt;br /&gt;
    // Do the sum of all new products/services prices and tva (will be incremented with every product/service line)&lt;br /&gt;
    $object-&amp;gt;total_ht += $line-&amp;gt;total_ht; // global no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva += $line-&amp;gt;total_tva; // global vat/tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc += $line-&amp;gt;total_ttc; // global  tax included price&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you should try to generate your PDF document, and you should get something like this:&lt;br /&gt;
[[File:Cfe3.jpg]]&lt;br /&gt;
&lt;br /&gt;
Note: printing the coefficient as a column in your PDF document is also possible but will not be covered here because PDF templates will vary for every persons depending on your needs.&lt;br /&gt;
For guidelines about how to do this, you can read the documentation on [[Create_a_PDF_document_template|how to create a PDF template]] and [[Module_CustomFields#Implementing_in_PDF_templates|how to use custom fields in PDF templates]].&lt;br /&gt;
&lt;br /&gt;
Hint: you should be looking inside the following conditional block, inside the write_file() method:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
{&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of total price in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
Unluckily there is not (yet) any hooking context to modify how lines of products/services are printed, thus the only thing you can do is directly edit the &#039;&#039;&#039;template file&#039;&#039;&#039; and manually put inside your code to load your custom field and print it the way you want using HTML formatting, for our example:&lt;br /&gt;
&lt;br /&gt;
* For Dolibarr 3.3 and above, edit the file &#039;&#039;&#039;htdocs/core/tpl/objectline_view.tpl.php&#039;&#039;&#039;&lt;br /&gt;
* For Dolibarr 3.1 and 3.2, edit the file &#039;&#039;&#039;htdocs/core/tpl/freeproductline_view.tpl.php&#039;&#039;&#039; AND &#039;&#039;&#039;predefinedproductline_view.tpl.php&#039;&#039;&#039; (apply the same changes to both files)&lt;br /&gt;
&lt;br /&gt;
In the template file you opened, find the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And replace it by:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $object-&amp;gt;table_element_line; // you can use $object-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
&lt;br /&gt;
// Modifying the HT price and printing it on the Dolibarr&#039;s datasheet interface&lt;br /&gt;
// Applying a multiplying/dividing coefficient to an HT price will give a mathematically correct TTC price since the taxes are also a multiplier&lt;br /&gt;
// However, if you want to apply other mathematical operations (like addition or substaction), you should use the total price with $line-&amp;gt;total_ttc&lt;br /&gt;
if (!empty($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
    echo price($line-&amp;gt;total_ht * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient);&lt;br /&gt;
} else {&lt;br /&gt;
    echo price($line-&amp;gt;total_ht);&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should produce something like this:&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Technical note: here we are using customfields_fill_object() instead of customfields_fill_object_lines() (the latter being specifically adapted to lines and thus should theoretically be used here) for performance reasons because the template file we are editing is called in a loop, and customfields_fill_object_lines() should be called outside a loop (because it already does the job that is done by the loop), so here we &#039;tricks&#039; the customfields_fill_object() function to avoid this loop by submitting &#039;facturedet&#039; as a module by itself (when in fact it&#039;s a product lines table, but it works the same way nevertheless). This is a good demonstration of the flexibility of the CustomFields functions and methods.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the coefficient column in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
It&#039;s also possible to show the coefficient in a new column on the Dolibarr interface.&lt;br /&gt;
&lt;br /&gt;
To do that, you need to do two things:&lt;br /&gt;
&lt;br /&gt;
1- Add the column title &amp;quot;Coeff.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
2- Add the generator that will input the coefficient value for each line.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Add the column title &amp;quot;Coeff.&amp;quot;&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
Open in your favourite editor the file /htdocs/core/class/commonobject.class.php and inside the function printObjectLines (note the plural s), find the following lines of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Total HT&lt;br /&gt;
print &#039;&amp;lt;td align=&amp;quot;right&amp;quot; width=&amp;quot;50&amp;quot;&amp;gt;&#039;.$langs-&amp;gt;trans(&#039;TotalHTShort&#039;).&#039;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is what prints the Total HT price column. We will place the Coeff column just before.&lt;br /&gt;
&lt;br /&gt;
So, just above this code, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
dol_include_once(&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element_line);&lt;br /&gt;
$coeff_field = $customfields-&amp;gt;fetchFieldStruct(&#039;coefficient&#039;);&lt;br /&gt;
if (!empty($coeff_field)) {&lt;br /&gt;
    print &#039;&amp;lt;td align=&amp;quot;right&amp;quot; width=&amp;quot;50&amp;quot;&amp;gt;&#039;.$langs-&amp;gt;trans(&#039;Coeff.&#039;).&#039;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will take care of showing the Coeff. column, but only for modules where you created such a custom field (because the file we modified is a common object for all modules in Dolibarr, not just invoices, thus this modification will gracefully skip this custom field if it does not exists for other modules).&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Add the generator&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/tpl/objectline_view.tpl.php and try to find the following block of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php if ($line-&amp;gt;special_code == 3)	{ ?&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo $langs-&amp;gt;trans(&#039;Option&#039;); ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } else { ?&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is what prints the total HT price value for each line.&lt;br /&gt;
&lt;br /&gt;
Just above of this block of code, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $object-&amp;gt;table_element_line; // you can use $this-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
if (isset($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
    &amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient; ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just like for the column title, this code will take care of generating the coefficient value only if the custom field is defined, because this file is also a generic template for all modules of Dolibarr.&lt;br /&gt;
&lt;br /&gt;
At this point, everything is done, you should see the following:&lt;br /&gt;
[[File:Cf_wiki_coeff_column.png]]&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in margin module ===&lt;br /&gt;
&lt;br /&gt;
If you enabled the margin module, you should see that the coefficient is not applied there, but it&#039;s easy to fix that.&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/class/commonobject.class.php and find the function getMarginInfos(), then inside find the following lines:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach($this-&amp;gt;lines as $line) {&lt;br /&gt;
    if (empty($line-&amp;gt;pa_ht) &amp;amp;&amp;amp; isset($line-&amp;gt;fk_fournprice) &amp;amp;&amp;amp; !$force_price) {&lt;br /&gt;
        $product = new ProductFournisseur($this-&amp;gt;db);&lt;br /&gt;
        if ($product-&amp;gt;fetch_product_fournisseur_price($line-&amp;gt;fk_fournprice))&lt;br /&gt;
            $line-&amp;gt;pa_ht = $product-&amp;gt;fourn_unitprice * (1 - $product-&amp;gt;fourn_remise_percent / 100);&lt;br /&gt;
        if (isset($conf-&amp;gt;global-&amp;gt;MARGIN_TYPE) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;MARGIN_TYPE == &amp;quot;2&amp;quot; &amp;amp;&amp;amp; $product-&amp;gt;fourn_unitcharges &amp;gt; 0)&lt;br /&gt;
            $line-&amp;gt;pa_ht += $product-&amp;gt;fourn_unitcharges;&lt;br /&gt;
    }&lt;br /&gt;
    // si prix d&#039;achat non renseigné et devrait l&#039;être, alors prix achat = prix vente&lt;br /&gt;
    if ((!isset($line-&amp;gt;pa_ht) || $line-&amp;gt;pa_ht == 0) &amp;amp;&amp;amp; $line-&amp;gt;subprice &amp;gt; 0 &amp;amp;&amp;amp; (isset($conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull == 1)) {&lt;br /&gt;
        $line-&amp;gt;pa_ht = $line-&amp;gt;subprice * (1 - ($line-&amp;gt;remise_percent / 100));&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the lines are filled with margin infos. The price has just been fetched here, and we will now modify it with the coefficient before the price gets used for margin computation.&lt;br /&gt;
&lt;br /&gt;
Just below the code we found, paste the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $line-&amp;gt;table_element; // you can use $this-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
if (!empty($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
    $line-&amp;gt;pa_ht = $line-&amp;gt;pa_ht * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient;&lt;br /&gt;
    $line-&amp;gt;subprice = $line-&amp;gt;subprice * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s all, the price should be reflected in the margin.&lt;br /&gt;
[[File:Cf_wiki_coeff_margin.png]]&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in remaining debt to pay ===&lt;br /&gt;
&lt;br /&gt;
Open /htdocs/core/class/commonobject.class.php and look for the function update_price().&lt;br /&gt;
&lt;br /&gt;
You should see the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function update_price($exclspec=0,$roundingadjust=&#039;none&#039;,$nodatabaseupdate=0,$seller=&#039;&#039;)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just below the include_once line, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;;&lt;br /&gt;
include_once DOL_DOCUMENT_ROOT.&#039;/customfields/conf/conf_customfields_func.lib.php&#039;;&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $this-&amp;gt;table_element_line);&lt;br /&gt;
// Fetching line ids from parent object id&lt;br /&gt;
$line_ids = $customfields-&amp;gt;fetchAny(&#039;rowid&#039;, MAIN_DB_PREFIX.$this-&amp;gt;table_element_line, $this-&amp;gt;fk_element.&#039; = &#039;.$this-&amp;gt;id);&lt;br /&gt;
$line_ids = array_values_recursive(&#039;rowid&#039;, $line_ids);&lt;br /&gt;
// Load custom fields&lt;br /&gt;
$cf_records = $customfields-&amp;gt;fetch($line_ids);&lt;br /&gt;
// Reassociate the records with the line id (instead of the customfields record id)&lt;br /&gt;
$cf_line_cfrowid_to_rowid = array_reassociate(&#039;rowid&#039;, &#039;fk_&#039;.$this-&amp;gt;table_element_line, $cf_records);&lt;br /&gt;
$tmp = $cf_records;&lt;br /&gt;
$cf_records = array();&lt;br /&gt;
foreach ($tmp as $k=&amp;gt;$v) { $cf_records[$cf_line_cfrowid_to_rowid[$k]] = $tmp[$k]; }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will load the coefficient custom field (and also any other custom field you have defined for lines).&lt;br /&gt;
&lt;br /&gt;
Now scroll below, and you should see this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
while ($i &amp;lt; $num)&lt;br /&gt;
{&lt;br /&gt;
    $obj = $this-&amp;gt;db-&amp;gt;fetch_object($resql);&lt;br /&gt;
&lt;br /&gt;
    // Note: There is no check on detail line and no check on total, if $forcedroundingmode = &#039;none&#039;&lt;br /&gt;
&lt;br /&gt;
    if ($forcedroundingmode == &#039;0&#039;)	// Check if data on line are consistent. This may solve lines that were not consistent because set with $forcedroundingmode=&#039;auto&#039;&lt;br /&gt;
    {&lt;br /&gt;
        $localtax_array=array($obj-&amp;gt;localtax1_type,$obj-&amp;gt;localtax1_tx,$obj-&amp;gt;localtax2_type,$obj-&amp;gt;localtax2_tx);&lt;br /&gt;
        $tmpcal=calcul_price_total($obj-&amp;gt;qty, $obj-&amp;gt;up, $obj-&amp;gt;remise_percent, $obj-&amp;gt;vatrate, $obj-&amp;gt;localtax1_tx, $obj-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $obj-&amp;gt;info_bits, $obj-&amp;gt;product_type, $seller, $localtax_array);&lt;br /&gt;
        $diff=price2num($tmpcal[1] - $obj-&amp;gt;total_tva, &#039;MT&#039;, 1);&lt;br /&gt;
        if ($diff)&lt;br /&gt;
        {&lt;br /&gt;
            $sqlfix=&amp;quot;UPDATE &amp;quot;.MAIN_DB_PREFIX.$this-&amp;gt;table_element_line.&amp;quot; SET &amp;quot;.$fieldtva.&amp;quot; = &amp;quot;.$tmpcal[1].&amp;quot;, total_ttc = &amp;quot;.$tmpcal[2].&amp;quot; WHERE rowid = &amp;quot;.$obj-&amp;gt;rowid;&lt;br /&gt;
            dol_syslog(&#039;We found unconsistent data into detailed line (difference of &#039;.$diff.&#039;) for line rowid = &#039;.$obj-&amp;gt;rowid.&amp;quot; (total vat of line calculated=&amp;quot;.$tmpcal[1].&amp;quot;, database=&amp;quot;.$obj-&amp;gt;total_tva.&amp;quot;). We fix the total_vat and total_ttc of line by running sqlfix = &amp;quot;.$sqlfix);&lt;br /&gt;
            $resqlfix=$this-&amp;gt;db-&amp;gt;query($sqlfix);&lt;br /&gt;
            if (! $resqlfix) dol_print_error($this-&amp;gt;db,&#039;Failed to update line&#039;);&lt;br /&gt;
            $obj-&amp;gt;total_tva = $tmpcal[1];&lt;br /&gt;
            $obj-&amp;gt;total_ttc = $tmpcal[2];&lt;br /&gt;
            //&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just after, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
$cf_coeff = $cf_records[$obj-&amp;gt;rowid]-&amp;gt;coefficient;&lt;br /&gt;
if (empty($cf_coeff) and $cf_coeff !== 0) $cf_coeff = 1; // by default, an empty coefficient = 1&lt;br /&gt;
$obj-&amp;gt;total_ht = $obj-&amp;gt;total_ht * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_tva = $obj-&amp;gt;total_tva * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_localtax1 = $obj-&amp;gt;total_localtax1 * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_localtax2 = $obj-&amp;gt;total_localtax2 * $cf_coeff;&lt;br /&gt;
$obj-&amp;gt;total_ttc = $obj-&amp;gt;total_ttc * $cf_coeff;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should do the trick. Now go to one of your invoices (or whatever module you are targetting), and you should see this:&lt;br /&gt;
[[File:Cf_wiki_coeff_remaining_debt.png.png]]&lt;br /&gt;
&lt;br /&gt;
= Custom tax per product =&lt;br /&gt;
&lt;br /&gt;
This part will show you how to create a static custom tax per product. Static means that the tax will be stored per product, and not per product&#039;s line, thus you just have to setup the tax once per product and it will always be applied in every invoice you add this product.&lt;br /&gt;
&lt;br /&gt;
To do that, you will need the CustomFields Pro module + ProductsEasyAccess.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
1- Create a product&#039;s custom fields called &amp;quot;custom_tax&amp;quot;. Now, using ProductsEasyAccess, you can access this field wherever you wants, either to show it on the Dolibarr&#039;s interface (see previous chapter), or in a PDF template, which we will describe below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2- Modify your invoice&#039;s PDF template. You first need to load all the products&#039; fields &#039;&#039;before&#039;&#039; the loop over the products&#039; lines. To do this, search for a line like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// This should be placed above the &amp;quot;loop lines&amp;quot; below, add this:&lt;br /&gt;
&lt;br /&gt;
dol_include_once(&#039;/productseasyaccess/lib/productseasyaccess.lib.php&#039;);&lt;br /&gt;
fill_object_with_products_fields($object);&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines, search for this command&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  // Add this just below the for loop, this will ease your php manipulation after.&lt;br /&gt;
  // This will allow you to more easily access the products&#039; fields (this will store the id of each product inside $lineproductid).&lt;br /&gt;
  $line_product_id = $object-&amp;gt;lines[$i]-&amp;gt;fk_product;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4- You can now use your products&#039; fields in your PDF. To access the custom_tax field, just use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And to print it in your PDF:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell(0,3, $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax, 0, &#039;L&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5- Then, if you want to count the custom tax total and add it to your vat total, you can create a counter inside the for loop we cited in the first step:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_custom_tax = 0; // Initialize the custom tax total counter&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  $object-&amp;gt;total_custom_tax += $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then find inside the function **_tableau_tot**, find the following line:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And prepend the total modification like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc + $object-&amp;gt;total_custom_tax; // prepend this to add the custom tax to the total&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Done. This should show your custom tax per product and your total including all custom taxes.&lt;/div&gt;</summary>
		<author><name>Lrq3000</name></author>
	</entry>
	<entry>
		<id>https://wiki.dolibarr.org/index.php?title=File:Cf_wiki_coeff_remaining_debt.png&amp;diff=33047</id>
		<title>File:Cf wiki coeff remaining debt.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php?title=File:Cf_wiki_coeff_remaining_debt.png&amp;diff=33047"/>
		<updated>2015-01-07T18:57:36Z</updated>

		<summary type="html">&lt;p&gt;Lrq3000: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Lrq3000</name></author>
	</entry>
	<entry>
		<id>https://wiki.dolibarr.org/index.php?title=File:Cf_wiki_coeff_margin.png&amp;diff=33046</id>
		<title>File:Cf wiki coeff margin.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php?title=File:Cf_wiki_coeff_margin.png&amp;diff=33046"/>
		<updated>2015-01-07T18:57:12Z</updated>

		<summary type="html">&lt;p&gt;Lrq3000: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Lrq3000</name></author>
	</entry>
	<entry>
		<id>https://wiki.dolibarr.org/index.php?title=File:Cf_wiki_coeff_column.png&amp;diff=33045</id>
		<title>File:Cf wiki coeff column.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php?title=File:Cf_wiki_coeff_column.png&amp;diff=33045"/>
		<updated>2015-01-07T18:56:11Z</updated>

		<summary type="html">&lt;p&gt;Lrq3000: uploaded a new version of &amp;amp;quot;File:Cf wiki coeff column.png&amp;amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Adding a column for the Coefficient custom field.&lt;/div&gt;</summary>
		<author><name>Lrq3000</name></author>
	</entry>
	<entry>
		<id>https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33044</id>
		<title>Module CustomFields Cases</title>
		<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33044"/>
		<updated>2015-01-07T16:40:24Z</updated>

		<summary type="html">&lt;p&gt;Lrq3000: /* Change printed total price per product without tampering the subprice in the database */ Adding margin implementation&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:CustomFields]]&lt;br /&gt;
{{TemplateDocDevEn}}&lt;br /&gt;
{{TemplateModEN}}&lt;br /&gt;
{{BasculeDevUserEn|&lt;br /&gt;
name=CustomFields|&lt;br /&gt;
num=8500|&lt;br /&gt;
devdoc=[[Module_CustomFields_Dev]]|&lt;br /&gt;
userdoc=[[Module_CustomFields]]|}}&lt;br /&gt;
This page lists a compilation of concrete, practical examples uses of [[Module_CustomFields|CustomFields]] and of its various features.&lt;br /&gt;
&lt;br /&gt;
If you had an experience with CustomFields, you can also add here your case as an example for others to get inspired.&lt;br /&gt;
&lt;br /&gt;
= How to make a generic PDF template listing all custom fields =&lt;br /&gt;
&lt;br /&gt;
To make a generic PDF template listing all custom fields, you just need to take an existing PDF template, then you need to follow these 3 steps:&lt;br /&gt;
&lt;br /&gt;
1/ Edit the class name (required, else it will conflict with the original template).&lt;br /&gt;
&lt;br /&gt;
Eg: if we take the crabe template for invoices, we change:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;crabe&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
into:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_customfields extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;customfields&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2/ Copy this generic function at the end of the PDF:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propale/order/etc... (CustomFields simpler functions will automatically adapt)&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs); // set default PDF font size&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
    // Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
    $customfields = customfields_fill_object($object, null, $outputlangs, null, true);&lt;br /&gt;
&lt;br /&gt;
    // Setting the starting position of the text cursor&lt;br /&gt;
    $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
    $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
    // Printing the customfields&lt;br /&gt;
    foreach ($object-&amp;gt;customfields as $label=&amp;gt;$value) { // $value is already formatted!&lt;br /&gt;
        // Get translated label&lt;br /&gt;
        $translatedlabel = $customfields-&amp;gt;findLabelPDF($label, $outputlangs); // translated label of the customfield (not translated by default in customfields_fill_object() because a field should always be accessible by a base name, whatever the translation is)&lt;br /&gt;
&lt;br /&gt;
        // PDF formatting, placement and printing&lt;br /&gt;
        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
        $pdf-&amp;gt;MultiCell(0,3, $translatedlabel.&#039; ($object-&amp;gt;customfields-&amp;gt;&#039;.$label.&#039;): &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternative function (with a bit less functionalities, but more controllable):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page (used to debug if CustomFields setup is correct)&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propal/product/whatever...&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs);&lt;br /&gt;
&lt;br /&gt;
            if (empty($object-&amp;gt;table_element) or empty($object-&amp;gt;id)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;Current \$object is not compatible with CustomFields, could not find table_element or id.&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
    $customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element);&lt;br /&gt;
&lt;br /&gt;
            // Fetching custom fields records&lt;br /&gt;
            $fields = $customfields-&amp;gt;fetch($object-&amp;gt;id);&lt;br /&gt;
&lt;br /&gt;
            if (!isset($fields)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;No custom field could be found for this object. Please check your configuration (did you create at least one customfield and set a value in the current datasheet?)&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            } else {&lt;br /&gt;
                // Setting the starting position of the text cursor&lt;br /&gt;
                $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
                $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
                // Printing the customfields&lt;br /&gt;
                foreach ($fields as $key=&amp;gt;$field) {&lt;br /&gt;
                        $translatedname = $customfields-&amp;gt;findLabelPDF($key, $outputlangs); // label of the customfield&lt;br /&gt;
                        $value = $customfields-&amp;gt;simpleprintFieldPDF($key, $field, $outputlangs); // value (cleaned and properly formatted) of the customfield&lt;br /&gt;
&lt;br /&gt;
                        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
                        $pdf-&amp;gt;MultiCell(0,3, $translatedname.&#039;: &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
                        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3/ Call the _pagecustomfields function at the right place (generally, after _pagefoot and just before $pdf-&amp;gt;Close()):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Pied de page&lt;br /&gt;
$this-&amp;gt;_pagefoot($pdf,$object,$outputlangs);&lt;br /&gt;
$pdf-&amp;gt;AliasNbPages();&lt;br /&gt;
&lt;br /&gt;
// CustomFields&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) { // if the customfields module is activated...&lt;br /&gt;
        // Add a page with the customfields&lt;br /&gt;
        $pdf-&amp;gt;AddPage();&lt;br /&gt;
        $this-&amp;gt;_pagecustomfields($pdf,$object,$outputlangs);&lt;br /&gt;
        $pagenb++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
$pdf-&amp;gt;Close(); // you should first search for this, you should only find one occurrence&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then go to the module&#039;s admin panel (eg: invoice admin panel) and enable the template you&#039;ve just created.&lt;br /&gt;
&lt;br /&gt;
You can now use it to test if your customfields work.&lt;br /&gt;
&lt;br /&gt;
= Conditions on fields selection =&lt;br /&gt;
&lt;br /&gt;
Sometimes you may want to show a DropdownBox or Constraint (or another multi-choice type), but you may want to show or hide choices based on conditions (eg: only if it&#039;s a prospect, only if this zone was selected, only if this category was selected, etc..).&lt;br /&gt;
&lt;br /&gt;
To know which is the best solution you should choose to implement your condition, you should first know in which category your condition falls in.&lt;br /&gt;
&lt;br /&gt;
== Conditions categories ==&lt;br /&gt;
&lt;br /&gt;
Conditions can be categorized into 4 types:&lt;br /&gt;
&lt;br /&gt;
* static condition: choices are restrained on a condition that never changes (eg: only show third-parties that are suppliers, or lowercase all data on sql insertion, or check that a field is above a certain number, etc.).&lt;br /&gt;
* semi-dynamic condition: choices that are constrained on a static table but based on the value of another field/customfield. This is also commonly called &amp;quot;Cascaded dropdown lists&amp;quot; or &amp;quot;Dynamically linked dropdown lists&amp;quot;.&lt;br /&gt;
* dynamic: everything is dynamic: choices are based on a subfield of a field of the current module, so that there&#039;s no material table and it&#039;s impossible to create a view (because you would need to create one view per id) (eg: show only the contacts attached to the third-party being accessed)&lt;br /&gt;
* timed condition: a condition that is based on time or recurrence (eg: at 5:00 AM delete old records, etc..)&lt;br /&gt;
&lt;br /&gt;
== Conditions solutions ==&lt;br /&gt;
&lt;br /&gt;
Now that you know what kind of condition you want to set, you can choose one of the following solutions depending on your preferences and knowledge.&lt;br /&gt;
&lt;br /&gt;
In any case, you can always use the &#039;&#039;&#039;CustomFields&#039;s overload functions&#039;&#039;&#039; to fit your needs for nearly every type of condition.&lt;br /&gt;
&lt;br /&gt;
=== Static condition ===&lt;br /&gt;
&lt;br /&gt;
* View: create a view on the table based on the condition, and then create a custom field constrained on this view (which is just like any table), eg: only show third-parties that are suppliers:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
CREATE VIEW llx_societe_supplier_view AS&lt;br /&gt;
SELECT *&lt;br /&gt;
FROM llx_societe&lt;br /&gt;
WHERE fournisseur=1;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
WARNING: foreign keys (and thus constraints) can&#039;t be used on views with MySQL, and materialized views neither exist, so you can&#039;t use this method with MySQL.&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
* SQL Transformations&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
=== Semi-dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade option is ideal in most cases, and if not enough, you can use the Cascade Custom option and Overloading Functions.&lt;br /&gt;
* View&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
&lt;br /&gt;
=== Dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade Custom and Overload functions&lt;br /&gt;
* Making your own module, which calls the CustomFields class&lt;br /&gt;
&lt;br /&gt;
=== Timed condition ===&lt;br /&gt;
&lt;br /&gt;
* SQL scheduled events&lt;br /&gt;
* Cron job&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
= Overloading functions =&lt;br /&gt;
&lt;br /&gt;
Here are a few concrete use cases of overloading functions to achieve various goals.&lt;br /&gt;
&lt;br /&gt;
== Dynamic list selection based on another one ==&lt;br /&gt;
&lt;br /&gt;
The goal is to show two custom fields on the Third-Party module: one which gives the zone (secteur) of the third-party (Isere, Alpes du sud, Haute-Savoie...) and the other which gives relative to the zone the list of all the ski resorts (station_a) inside the selected zone.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will show how to manually manage this kind of requirements using [[Module_CustomFields#Overloading_functions:_Adding_your_own_management_code_for_custom_fields|overloading functions]], but in latest CustomFields releases, the same solution can be achieved automatically using the [[Module_CustomFields#Cascade|Cascade option]].&lt;br /&gt;
&lt;br /&gt;
We have one table &#039;&#039;&#039;llx_k_station&#039;&#039;&#039; (rowid, station (char50), secteur(char50)) which contains the following: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1,les 2 alpes,isère&lt;br /&gt;
2,chamrousse,isère&lt;br /&gt;
3,alpe d&#039;huez,isère&lt;br /&gt;
4,vars,alpes du sud&lt;br /&gt;
5,risoul,alpes du sud&lt;br /&gt;
etc...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In CustomFields&#039;s admin panel, we create two custom fields:&lt;br /&gt;
&lt;br /&gt;
1- secteur: returns the list of all zones - type DropdownBox: enum(&#039;- Aucun&#039;,&#039;Alpes du Sud&#039;,&#039;export&#039;,&#039;Haute-Savoie&#039;,&#039;Isère&#039;, etc...)&lt;br /&gt;
&lt;br /&gt;
2- station_a: returns the list of all ski resorts - type constraint on &#039;&#039;&#039;llx_k_station&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Here is the code to put in customfields_fields_extend.lib.php that will allow to show only the ski resorts corresponding to the selected zone:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_editview_societe_station_a (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$parameters, &amp;amp;$action, &amp;amp;$user, &amp;amp;$idvar, &amp;amp;$rightok, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
  global $db; // allow to access database functions&lt;br /&gt;
  $sql=&amp;quot;SELECT llx_k_station.station, llx_k_station.rowid FROM llx_societe_customfields INNER JOIN llx_k_station ON llx_societe_customfields.Secteur = llx_k_station.secteur WHERE llx_societe_customfields.fk_societe=&amp;quot;.$object-&amp;gt;id;&lt;br /&gt;
$result=$db-&amp;gt;query($sql);&lt;br /&gt;
 &lt;br /&gt;
  if ($result) {&lt;br /&gt;
    $num = $db-&amp;gt;num_rows($result);&lt;br /&gt;
    $i = 0;&lt;br /&gt;
    $myarray = array();&lt;br /&gt;
    while ($i &amp;lt; $num)&lt;br /&gt;
    {&lt;br /&gt;
      $obj = $db-&amp;gt;fetch_object($result);&lt;br /&gt;
      $myarray []=array(&#039;id&#039;=&amp;gt;$obj-&amp;gt;rowid, &#039;value&#039;=&amp;gt;$obj-&amp;gt;station);&lt;br /&gt;
      $i++;&lt;br /&gt;
    }&lt;br /&gt;
    $value = $myarray; &lt;br /&gt;
    $db-&amp;gt;free($result);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to manub for giving this case.&lt;br /&gt;
&lt;br /&gt;
== Dynamic discount computation per products lines on propales ==&lt;br /&gt;
&lt;br /&gt;
The goal here is to compute the discount for a product line in propales based on a computation on three custom fields.&lt;br /&gt;
&lt;br /&gt;
First, create three custom fields for &amp;quot;Line commercial proposals&amp;quot;: vl, cpl and lc.&lt;br /&gt;
&lt;br /&gt;
Then add the following overloading function at the end of your customfields_fields_extend.lib.php file (read the comments for more informations):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_propaldet_vl (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
&lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $vl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;vl&#039;};&lt;br /&gt;
    $cpl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;cpl&#039;};&lt;br /&gt;
    $lc = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;lc&#039;};&lt;br /&gt;
&lt;br /&gt;
    // Compute the discount&lt;br /&gt;
    $remise_calculee = (100 - $vl) * (100 - $cpl) * (100 - $lc);&lt;br /&gt;
&lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $remise_calculee = price2num($remise_calculee);&lt;br /&gt;
&lt;br /&gt;
    // Assign the new discount into the PropalLine object&lt;br /&gt;
    $object-&amp;gt;remise_percent = $remise_calculee;&lt;br /&gt;
&lt;br /&gt;
    // Update the price (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
&lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
&lt;br /&gt;
    //$q = &amp;quot;UPDATE  &amp;quot;.MAIN_DB_PREFIX.&amp;quot;propaldet SET remise_percent=&amp;quot;.$remise_calculee.&amp;quot; WHERE  rowid=&amp;quot;.$object-&amp;gt;rowid;&lt;br /&gt;
    //$db-&amp;gt;query($q);&lt;br /&gt;
    &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;vl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;cpl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;lc&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Linking Dolibarr objects from two different modules =&lt;br /&gt;
&lt;br /&gt;
Dolibarr sometimes offers the possibility to link two different modules. Eg: when you convert an Intervention card into an Invoice.&lt;br /&gt;
&lt;br /&gt;
In this case, you might want in your templates to fetch the custom fields of the linked object. Eg: you generate a PDF from the Invoice, but you want to get the custom fields from the Intervention that was converted into the Invoice.&lt;br /&gt;
&lt;br /&gt;
The key here is to use the &#039;&#039;&#039;llx_element_element&#039;&#039;&#039; table or the &#039;&#039;&#039;llx_element_*&#039;&#039;&#039; tables (eg: llx_element_contact): this is a standard Dolibarr table that stores all links between objects of two different modules.&lt;br /&gt;
&lt;br /&gt;
Here is the structure:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
rowid | fk_source | sourcetype | fk_target | targettype&lt;br /&gt;
-------------------------------------------------------&lt;br /&gt;
1 | 2 | fichinter | 5 | facture&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can then use a php code like the following to populate your $object with customfields:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
// This will also create and returns us an instance of the CustomFields class, which we will use to manually query the llx_element_element table&lt;br /&gt;
$customfields = customfields_fill_object($object, null, $outputlangs, &#039;facture&#039;, true); // beautified values&lt;br /&gt;
&lt;br /&gt;
// Querying the llx_element_element table manually to get the link between the invoice and the linked intervention card (we want the id of the intervention card)&lt;br /&gt;
// fetchAny($columns, $table, $where=&#039;&#039;, $orderby=&#039;&#039;, $limitby=&#039;&#039;)&lt;br /&gt;
$eltrow = $customfields-&amp;gt;fetchAny(&#039;*&#039;, MAIN_DB_PREFIX.&#039;element_element&#039;, &#039;targettype=&amp;quot;&#039;.$object-&amp;gt;element.&#039;&amp;quot; and fk_target=&#039;.$object-&amp;gt;id.&#039; and sourcetype=&amp;quot;fichinter&amp;quot;&#039;);&lt;br /&gt;
&lt;br /&gt;
// Save the intervention&#039;s id&lt;br /&gt;
$sourceid = $eltrow[0][&#039;fk_source&#039;];&lt;br /&gt;
&lt;br /&gt;
// Crafting a dummy intervention object&lt;br /&gt;
$fromobject = new stdClass(); // Init an empty object&lt;br /&gt;
$fromobject-&amp;gt;id = $sourceid; // We need the id and..&lt;br /&gt;
$fromobject-&amp;gt;table_element = &#039;fichinter&#039;; // the table_element (name of the table)&lt;br /&gt;
&lt;br /&gt;
// We can then fill $object with the intervention custom fields (we store them in a sub property &#039;fichinter&#039; to avoid conflicts with invoice&#039;s custom fields)&lt;br /&gt;
customfields_fill_object($object, $fromobject, $outputlangs, &#039;fichinter&#039;, true);&lt;br /&gt;
&lt;br /&gt;
// Now you can access invoices custom fields using:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;facture-&amp;gt;cf_myfield);&lt;br /&gt;
// And access intervention custom fields with:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;fichinter-&amp;gt;cf_myfield);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to netassopro for the tip.&lt;br /&gt;
&lt;br /&gt;
= Modify the total price of every product/service line with a coefficient (without tampering the discount) =&lt;br /&gt;
&lt;br /&gt;
Here we will go through the creation and management of a custom field called &#039;&#039;&#039;coefficient&#039;&#039;&#039; that will change the total price of each product by modifying it by the coefficient.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will not create a coefficient per product but rather per product&#039;s line. Thus if you want to create a coefficient that never change between invoices but only depending on the product and stays static, please refer to the next chapter below about creating a custom tax per product.&lt;br /&gt;
&lt;br /&gt;
The preliminary and necessary step is to first create a custom field that will be used to modify the total price. We will create one for Client Invoice Lines.&lt;br /&gt;
&lt;br /&gt;
Go to the CustomFields administration panel, then into the Client Invoice Lines tab, then click on the button New Field, name the field &#039;&#039;&#039;coefficient&#039;&#039;&#039;, and set the type to &#039;&#039;&#039;Double&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
When you go to client invoices in Dolibarr, you should then see something similar to this:&lt;br /&gt;
[[File:Cfe1.jpg]]&lt;br /&gt;
You can see that our coefficient field was successfully created.&lt;br /&gt;
&lt;br /&gt;
Then here are two alternative ways of implementing the change of the total price of each product relativery to this coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change total price in database ==&lt;br /&gt;
&lt;br /&gt;
Here we will use a &amp;quot;save&amp;quot; overloading function to change the total price directly in the database, thus we won&#039;t have to modify anything else (eg: nor PDF templating nor ODT) since the total price will be already updated in the database.&lt;br /&gt;
&lt;br /&gt;
Warning: this method will work only with CustomFields =&amp;gt; 3.2.23 since the &amp;quot;save&amp;quot; overloading function did not work for products lines before.&lt;br /&gt;
&lt;br /&gt;
Just add the following overloading function in your customfields_fields_extend.lib.php file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_facturedet_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
 &lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $coefficient = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;coefficient&#039;}; // you could also use $coefficient = $value;&lt;br /&gt;
 &lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $coefficient = price2num($coefficient);&lt;br /&gt;
 &lt;br /&gt;
    // Get the original subprices (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
 &lt;br /&gt;
    // Update the subprices with the coefficient&lt;br /&gt;
    $object-&amp;gt;total_ht = $object-&amp;gt;total_ht * $coefficient; // compute no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva = $object-&amp;gt;total_tva * $coefficient; // compute tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc * $coefficient; // compute tax included price&lt;br /&gt;
 &lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(&#039;&#039;,true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
 &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;coefficient&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should show the correct total price on the Dolibarr&#039;s interface without modifying the unit subprice (P.U. Unit):&lt;br /&gt;
&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
You have nothing to edit in your PDF or ODT template, since the new subprice with applied coefficient will be automatically printed instead of the old one without coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change printed total price per product without tampering the subprice in the database ==&lt;br /&gt;
&lt;br /&gt;
Here is a way to change the printed total price (the one shown on screen and in PDF/ODT) but without changing the total price in the database.&lt;br /&gt;
&lt;br /&gt;
The following sub-chapters are all independent, thus you can only follow the ones that you require, eg: if you only need to change the price in your PDF documents, just create a custom field then jump to the sub-chapter to &#039;&#039;&#039;Implement the change of price in PDF&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in ODT templates ===&lt;br /&gt;
&lt;br /&gt;
There&#039;s no eval implementation currently inside ODT templates, but there is a kind of hook implementation, called [[Create_an_ODT_document_template#Other_personalized_tags|substitution functions]].&lt;br /&gt;
&lt;br /&gt;
You can either create your own module, then create your own substitution function, but for the sake of simplicity in this tutorial, we will directly use the CustomFields substitution function. However, please remember that you will have to redo your changes everytime you will update the CustomFields module, so if you don&#039;t want to do that it would be better if you create your own module and substitution function.&lt;br /&gt;
&lt;br /&gt;
Open the file &#039;&#039;&#039;htdocs/customfields/core/substitutions/functions_customfields.lib.php&#039;&#039;&#039; and go to the function &#039;&#039;&#039;customfields_completesubstitutionarray_lines&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
At the very end of this function, but inside the customfields check:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) {&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
But just before the ending bracket, you should add the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($object-&amp;gt;table_element_line === &#039;facturedet&#039;) { // First check that we are in the right module, we don&#039;t want to use coefficient for other modules if we didn&#039;t define the custom field there&lt;br /&gt;
    $substitutionarray[&#039;line_price_full&#039;] = $substitutionarray[&#039;line_price_ttc&#039;] * $substitutionarray[&#039;cf_coefficient&#039;]; // multiply the total price with our coefficient&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code will add a new tag &#039;&#039;&#039;{line_price_full}&#039;&#039;&#039; that you can then use inside your ODT templates.&lt;br /&gt;
&lt;br /&gt;
Just place this tag in a table in your ODT document, between the table tags ( [!-- BEGIN row.lines --] ... [!-- END row.lines --] ), and this will print your full price.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in PDF templates ===&lt;br /&gt;
&lt;br /&gt;
First, we will create our own PDF template by copying an existing template, and then we will edit it to change the price of items.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Creating your own PDF template by copying an existing template&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Go into the following folder: &#039;&#039;&#039;htdocs/core/modules/facture/doc/&#039;&#039;&#039;, then copy the file &#039;&#039;&#039;pdf_crabe.modules.php&#039;&#039;&#039; and name the new one &#039;&#039;&#039;pdf_crabecoeff.modules.php&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Then open it in your favourite editor, and change the following lines:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures // change pdf_crabe into pdf_crabecoeff&lt;br /&gt;
{&lt;br /&gt;
...&lt;br /&gt;
    function __construct($db)&lt;br /&gt;
    {&lt;br /&gt;
        ...&lt;br /&gt;
	$this-&amp;gt;name = &amp;quot;crabe&amp;quot;; // change &amp;quot;crabe&amp;quot; into &amp;quot;crabecoeff&amp;quot;&lt;br /&gt;
        ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you have your own PDF template!&lt;br /&gt;
&lt;br /&gt;
Now you should &#039;&#039;&#039;enable your PDF template&#039;&#039;&#039; before doing anything else, by going into the Modules Administration panel, and into the Invoice admin page, then click on the ON button at the right of the &#039;&#039;&#039;crabecoeff&#039;&#039;&#039; entry to enable the template.&lt;br /&gt;
&lt;br /&gt;
Now, let&#039;s proceed onto modifying the prices.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Changing the prices automatically in your PDF&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
You can now do pretty much anything you want in your PDF template, but I will give you a mean to automatically change the prices of every products with our coefficient, as well as the total price, without editing too much the template.&lt;br /&gt;
&lt;br /&gt;
First, try to find the following code of line (should be inside the write_file() method):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf=pdf_getInstance($this-&amp;gt;format);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, just copy/paste the following code &#039;&#039;&#039;below&#039;&#039;&#039; pdf_getInstance():&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$this-&amp;gt;customfields = customfields_fill_object($object, null, $outputlangs, null, true); // beautified values&lt;br /&gt;
$this-&amp;gt;customfields_raw = customfields_fill_object($object, null, $outputlangs, &#039;raw&#039;, null); // raw values&lt;br /&gt;
$this-&amp;gt;customfields_lines = customfields_fill_object_lines($object, null, $outputlangs, null, true); // product lines&#039; values&lt;br /&gt;
&lt;br /&gt;
// Resetting every $object price because we will compute them anew&lt;br /&gt;
$object-&amp;gt;total_ht = 0;&lt;br /&gt;
$object-&amp;gt;total_tva = 0;&lt;br /&gt;
$object-&amp;gt;total_ttc = 0;&lt;br /&gt;
// For each line of product/service, we will recompute all the prices&lt;br /&gt;
foreach($object-&amp;gt;lines as $line) {&lt;br /&gt;
    // Get the coefficient custom field&#039;s value for the current line&lt;br /&gt;
    $coeff = $object-&amp;gt;customfields-&amp;gt;lines-&amp;gt;{$line-&amp;gt;rowid}-&amp;gt;cf_coefficient;&lt;br /&gt;
    // Recompute the price of this product/service line&lt;br /&gt;
    $line-&amp;gt;total_ht = $line-&amp;gt;total_ht*$coeff; // compute no tax price&lt;br /&gt;
    $line-&amp;gt;total_tva = $line-&amp;gt;total_tva*$coeff; // compute tax amount&lt;br /&gt;
    $line-&amp;gt;total_ttc = $line-&amp;gt;total_ttc*$coeff; // compute tax included price&lt;br /&gt;
    // Do the sum of all new products/services prices and tva (will be incremented with every product/service line)&lt;br /&gt;
    $object-&amp;gt;total_ht += $line-&amp;gt;total_ht; // global no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva += $line-&amp;gt;total_tva; // global vat/tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc += $line-&amp;gt;total_ttc; // global  tax included price&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you should try to generate your PDF document, and you should get something like this:&lt;br /&gt;
[[File:Cfe3.jpg]]&lt;br /&gt;
&lt;br /&gt;
Note: printing the coefficient as a column in your PDF document is also possible but will not be covered here because PDF templates will vary for every persons depending on your needs.&lt;br /&gt;
For guidelines about how to do this, you can read the documentation on [[Create_a_PDF_document_template|how to create a PDF template]] and [[Module_CustomFields#Implementing_in_PDF_templates|how to use custom fields in PDF templates]].&lt;br /&gt;
&lt;br /&gt;
Hint: you should be looking inside the following conditional block, inside the write_file() method:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
{&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of total price in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
Unluckily there is not (yet) any hooking context to modify how lines of products/services are printed, thus the only thing you can do is directly edit the &#039;&#039;&#039;template file&#039;&#039;&#039; and manually put inside your code to load your custom field and print it the way you want using HTML formatting, for our example:&lt;br /&gt;
&lt;br /&gt;
* For Dolibarr 3.3 and above, edit the file &#039;&#039;&#039;htdocs/core/tpl/objectline_view.tpl.php&#039;&#039;&#039;&lt;br /&gt;
* For Dolibarr 3.1 and 3.2, edit the file &#039;&#039;&#039;htdocs/core/tpl/freeproductline_view.tpl.php&#039;&#039;&#039; AND &#039;&#039;&#039;predefinedproductline_view.tpl.php&#039;&#039;&#039; (apply the same changes to both files)&lt;br /&gt;
&lt;br /&gt;
In the template file you opened, find the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And replace it by:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $object-&amp;gt;table_element_line; // you can use $object-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
&lt;br /&gt;
// Modifying the HT price and printing it on the Dolibarr&#039;s datasheet interface&lt;br /&gt;
// Applying a multiplying/dividing coefficient to an HT price will give a mathematically correct TTC price since the taxes are also a multiplier&lt;br /&gt;
// However, if you want to apply other mathematical operations (like addition or substaction), you should use the total price with $line-&amp;gt;total_ttc&lt;br /&gt;
if (!empty($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
    echo price($line-&amp;gt;total_ht * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient);&lt;br /&gt;
} else {&lt;br /&gt;
    echo price($line-&amp;gt;total_ht);&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should produce something like this:&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Technical note: here we are using customfields_fill_object() instead of customfields_fill_object_lines() (the latter being specifically adapted to lines and thus should theoretically be used here) for performance reasons because the template file we are editing is called in a loop, and customfields_fill_object_lines() should be called outside a loop (because it already does the job that is done by the loop), so here we &#039;tricks&#039; the customfields_fill_object() function to avoid this loop by submitting &#039;facturedet&#039; as a module by itself (when in fact it&#039;s a product lines table, but it works the same way nevertheless). This is a good demonstration of the flexibility of the CustomFields functions and methods.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the coefficient column in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
It&#039;s also possible to show the coefficient in a new column on the Dolibarr interface.&lt;br /&gt;
&lt;br /&gt;
To do that, you need to do two things:&lt;br /&gt;
&lt;br /&gt;
1- Add the column title &amp;quot;Coeff.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
2- Add the generator that will input the coefficient value for each line.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Add the column title &amp;quot;Coeff.&amp;quot;&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
Open in your favourite editor the file /htdocs/core/class/commonobject.class.php and inside the function printObjectLines (note the plural s), find the following lines of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Total HT&lt;br /&gt;
print &#039;&amp;lt;td align=&amp;quot;right&amp;quot; width=&amp;quot;50&amp;quot;&amp;gt;&#039;.$langs-&amp;gt;trans(&#039;TotalHTShort&#039;).&#039;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is what prints the Total HT price column. We will place the Coeff column just before.&lt;br /&gt;
&lt;br /&gt;
So, just above this code, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
dol_include_once(&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element_line);&lt;br /&gt;
$coeff_field = $customfields-&amp;gt;fetchFieldStruct(&#039;coefficient&#039;);&lt;br /&gt;
if (!empty($coeff_field)) {&lt;br /&gt;
    print &#039;&amp;lt;td align=&amp;quot;right&amp;quot; width=&amp;quot;50&amp;quot;&amp;gt;&#039;.$langs-&amp;gt;trans(&#039;Coeff.&#039;).&#039;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will take care of showing the Coeff. column, but only for modules where you created such a custom field (because the file we modified is a common object for all modules in Dolibarr, not just invoices, thus this modification will gracefully skip this custom field if it does not exists for other modules).&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Add the generator&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/tpl/objectline_view.tpl.php and try to find the following block of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php if ($line-&amp;gt;special_code == 3)	{ ?&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo $langs-&amp;gt;trans(&#039;Option&#039;); ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } else { ?&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is what prints the total HT price value for each line.&lt;br /&gt;
&lt;br /&gt;
Just above of this block of code, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $object-&amp;gt;table_element_line; // you can use $this-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
if (isset($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
    &amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient; ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just like for the column title, this code will take care of generating the coefficient value only if the custom field is defined, because this file is also a generic template for all modules of Dolibarr.&lt;br /&gt;
&lt;br /&gt;
At this point, everything is done, you should see the following:&lt;br /&gt;
[[File:Cf_wiki_coeff_column.png]]&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in margin module ===&lt;br /&gt;
&lt;br /&gt;
If you enabled the margin module, you should see that the coefficient is not applied there, but it&#039;s easy to fix that.&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/class/commonobject.class.php and find the function getMarginInfos(), then inside find the following lines:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
foreach($this-&amp;gt;lines as $line) {&lt;br /&gt;
    if (empty($line-&amp;gt;pa_ht) &amp;amp;&amp;amp; isset($line-&amp;gt;fk_fournprice) &amp;amp;&amp;amp; !$force_price) {&lt;br /&gt;
        $product = new ProductFournisseur($this-&amp;gt;db);&lt;br /&gt;
        if ($product-&amp;gt;fetch_product_fournisseur_price($line-&amp;gt;fk_fournprice))&lt;br /&gt;
            $line-&amp;gt;pa_ht = $product-&amp;gt;fourn_unitprice * (1 - $product-&amp;gt;fourn_remise_percent / 100);&lt;br /&gt;
        if (isset($conf-&amp;gt;global-&amp;gt;MARGIN_TYPE) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;MARGIN_TYPE == &amp;quot;2&amp;quot; &amp;amp;&amp;amp; $product-&amp;gt;fourn_unitcharges &amp;gt; 0)&lt;br /&gt;
            $line-&amp;gt;pa_ht += $product-&amp;gt;fourn_unitcharges;&lt;br /&gt;
    }&lt;br /&gt;
    // si prix d&#039;achat non renseigné et devrait l&#039;être, alors prix achat = prix vente&lt;br /&gt;
    if ((!isset($line-&amp;gt;pa_ht) || $line-&amp;gt;pa_ht == 0) &amp;amp;&amp;amp; $line-&amp;gt;subprice &amp;gt; 0 &amp;amp;&amp;amp; (isset($conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull) &amp;amp;&amp;amp; $conf-&amp;gt;global-&amp;gt;ForceBuyingPriceIfNull == 1)) {&lt;br /&gt;
        $line-&amp;gt;pa_ht = $line-&amp;gt;subprice * (1 - ($line-&amp;gt;remise_percent / 100));&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is where the lines are filled with margin infos. The price has just been fetched here, and we will now modify it with the coefficient before the price gets used for margin computation.&lt;br /&gt;
&lt;br /&gt;
Just below the code we found, paste the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $line-&amp;gt;table_element; // you can use $this-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
if (!empty($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
    $line-&amp;gt;pa_ht = $line-&amp;gt;pa_ht * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient;&lt;br /&gt;
    $line-&amp;gt;subprice = $line-&amp;gt;subprice * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s all, the price should be reflected in the margin.&lt;br /&gt;
&lt;br /&gt;
= Custom tax per product =&lt;br /&gt;
&lt;br /&gt;
This part will show you how to create a static custom tax per product. Static means that the tax will be stored per product, and not per product&#039;s line, thus you just have to setup the tax once per product and it will always be applied in every invoice you add this product.&lt;br /&gt;
&lt;br /&gt;
To do that, you will need the CustomFields Pro module + ProductsEasyAccess.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
1- Create a product&#039;s custom fields called &amp;quot;custom_tax&amp;quot;. Now, using ProductsEasyAccess, you can access this field wherever you wants, either to show it on the Dolibarr&#039;s interface (see previous chapter), or in a PDF template, which we will describe below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2- Modify your invoice&#039;s PDF template. You first need to load all the products&#039; fields &#039;&#039;before&#039;&#039; the loop over the products&#039; lines. To do this, search for a line like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// This should be placed above the &amp;quot;loop lines&amp;quot; below, add this:&lt;br /&gt;
&lt;br /&gt;
dol_include_once(&#039;/productseasyaccess/lib/productseasyaccess.lib.php&#039;);&lt;br /&gt;
fill_object_with_products_fields($object);&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines, search for this command&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  // Add this just below the for loop, this will ease your php manipulation after.&lt;br /&gt;
  // This will allow you to more easily access the products&#039; fields (this will store the id of each product inside $lineproductid).&lt;br /&gt;
  $line_product_id = $object-&amp;gt;lines[$i]-&amp;gt;fk_product;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4- You can now use your products&#039; fields in your PDF. To access the custom_tax field, just use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And to print it in your PDF:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell(0,3, $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax, 0, &#039;L&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5- Then, if you want to count the custom tax total and add it to your vat total, you can create a counter inside the for loop we cited in the first step:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_custom_tax = 0; // Initialize the custom tax total counter&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  $object-&amp;gt;total_custom_tax += $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then find inside the function **_tableau_tot**, find the following line:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And prepend the total modification like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc + $object-&amp;gt;total_custom_tax; // prepend this to add the custom tax to the total&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Done. This should show your custom tax per product and your total including all custom taxes.&lt;/div&gt;</summary>
		<author><name>Lrq3000</name></author>
	</entry>
	<entry>
		<id>https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33043</id>
		<title>Module CustomFields Cases</title>
		<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33043"/>
		<updated>2015-01-07T16:11:45Z</updated>

		<summary type="html">&lt;p&gt;Lrq3000: /* Implementing the change of total price in the Dolibarr interface */ Better code again...&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:CustomFields]]&lt;br /&gt;
{{TemplateDocDevEn}}&lt;br /&gt;
{{TemplateModEN}}&lt;br /&gt;
{{BasculeDevUserEn|&lt;br /&gt;
name=CustomFields|&lt;br /&gt;
num=8500|&lt;br /&gt;
devdoc=[[Module_CustomFields_Dev]]|&lt;br /&gt;
userdoc=[[Module_CustomFields]]|}}&lt;br /&gt;
This page lists a compilation of concrete, practical examples uses of [[Module_CustomFields|CustomFields]] and of its various features.&lt;br /&gt;
&lt;br /&gt;
If you had an experience with CustomFields, you can also add here your case as an example for others to get inspired.&lt;br /&gt;
&lt;br /&gt;
= How to make a generic PDF template listing all custom fields =&lt;br /&gt;
&lt;br /&gt;
To make a generic PDF template listing all custom fields, you just need to take an existing PDF template, then you need to follow these 3 steps:&lt;br /&gt;
&lt;br /&gt;
1/ Edit the class name (required, else it will conflict with the original template).&lt;br /&gt;
&lt;br /&gt;
Eg: if we take the crabe template for invoices, we change:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;crabe&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
into:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_customfields extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;customfields&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2/ Copy this generic function at the end of the PDF:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propale/order/etc... (CustomFields simpler functions will automatically adapt)&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs); // set default PDF font size&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
    // Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
    $customfields = customfields_fill_object($object, null, $outputlangs, null, true);&lt;br /&gt;
&lt;br /&gt;
    // Setting the starting position of the text cursor&lt;br /&gt;
    $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
    $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
    // Printing the customfields&lt;br /&gt;
    foreach ($object-&amp;gt;customfields as $label=&amp;gt;$value) { // $value is already formatted!&lt;br /&gt;
        // Get translated label&lt;br /&gt;
        $translatedlabel = $customfields-&amp;gt;findLabelPDF($label, $outputlangs); // translated label of the customfield (not translated by default in customfields_fill_object() because a field should always be accessible by a base name, whatever the translation is)&lt;br /&gt;
&lt;br /&gt;
        // PDF formatting, placement and printing&lt;br /&gt;
        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
        $pdf-&amp;gt;MultiCell(0,3, $translatedlabel.&#039; ($object-&amp;gt;customfields-&amp;gt;&#039;.$label.&#039;): &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternative function (with a bit less functionalities, but more controllable):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page (used to debug if CustomFields setup is correct)&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propal/product/whatever...&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs);&lt;br /&gt;
&lt;br /&gt;
            if (empty($object-&amp;gt;table_element) or empty($object-&amp;gt;id)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;Current \$object is not compatible with CustomFields, could not find table_element or id.&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
    $customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element);&lt;br /&gt;
&lt;br /&gt;
            // Fetching custom fields records&lt;br /&gt;
            $fields = $customfields-&amp;gt;fetch($object-&amp;gt;id);&lt;br /&gt;
&lt;br /&gt;
            if (!isset($fields)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;No custom field could be found for this object. Please check your configuration (did you create at least one customfield and set a value in the current datasheet?)&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            } else {&lt;br /&gt;
                // Setting the starting position of the text cursor&lt;br /&gt;
                $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
                $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
                // Printing the customfields&lt;br /&gt;
                foreach ($fields as $key=&amp;gt;$field) {&lt;br /&gt;
                        $translatedname = $customfields-&amp;gt;findLabelPDF($key, $outputlangs); // label of the customfield&lt;br /&gt;
                        $value = $customfields-&amp;gt;simpleprintFieldPDF($key, $field, $outputlangs); // value (cleaned and properly formatted) of the customfield&lt;br /&gt;
&lt;br /&gt;
                        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
                        $pdf-&amp;gt;MultiCell(0,3, $translatedname.&#039;: &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
                        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3/ Call the _pagecustomfields function at the right place (generally, after _pagefoot and just before $pdf-&amp;gt;Close()):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Pied de page&lt;br /&gt;
$this-&amp;gt;_pagefoot($pdf,$object,$outputlangs);&lt;br /&gt;
$pdf-&amp;gt;AliasNbPages();&lt;br /&gt;
&lt;br /&gt;
// CustomFields&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) { // if the customfields module is activated...&lt;br /&gt;
        // Add a page with the customfields&lt;br /&gt;
        $pdf-&amp;gt;AddPage();&lt;br /&gt;
        $this-&amp;gt;_pagecustomfields($pdf,$object,$outputlangs);&lt;br /&gt;
        $pagenb++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
$pdf-&amp;gt;Close(); // you should first search for this, you should only find one occurrence&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then go to the module&#039;s admin panel (eg: invoice admin panel) and enable the template you&#039;ve just created.&lt;br /&gt;
&lt;br /&gt;
You can now use it to test if your customfields work.&lt;br /&gt;
&lt;br /&gt;
= Conditions on fields selection =&lt;br /&gt;
&lt;br /&gt;
Sometimes you may want to show a DropdownBox or Constraint (or another multi-choice type), but you may want to show or hide choices based on conditions (eg: only if it&#039;s a prospect, only if this zone was selected, only if this category was selected, etc..).&lt;br /&gt;
&lt;br /&gt;
To know which is the best solution you should choose to implement your condition, you should first know in which category your condition falls in.&lt;br /&gt;
&lt;br /&gt;
== Conditions categories ==&lt;br /&gt;
&lt;br /&gt;
Conditions can be categorized into 4 types:&lt;br /&gt;
&lt;br /&gt;
* static condition: choices are restrained on a condition that never changes (eg: only show third-parties that are suppliers, or lowercase all data on sql insertion, or check that a field is above a certain number, etc.).&lt;br /&gt;
* semi-dynamic condition: choices that are constrained on a static table but based on the value of another field/customfield. This is also commonly called &amp;quot;Cascaded dropdown lists&amp;quot; or &amp;quot;Dynamically linked dropdown lists&amp;quot;.&lt;br /&gt;
* dynamic: everything is dynamic: choices are based on a subfield of a field of the current module, so that there&#039;s no material table and it&#039;s impossible to create a view (because you would need to create one view per id) (eg: show only the contacts attached to the third-party being accessed)&lt;br /&gt;
* timed condition: a condition that is based on time or recurrence (eg: at 5:00 AM delete old records, etc..)&lt;br /&gt;
&lt;br /&gt;
== Conditions solutions ==&lt;br /&gt;
&lt;br /&gt;
Now that you know what kind of condition you want to set, you can choose one of the following solutions depending on your preferences and knowledge.&lt;br /&gt;
&lt;br /&gt;
In any case, you can always use the &#039;&#039;&#039;CustomFields&#039;s overload functions&#039;&#039;&#039; to fit your needs for nearly every type of condition.&lt;br /&gt;
&lt;br /&gt;
=== Static condition ===&lt;br /&gt;
&lt;br /&gt;
* View: create a view on the table based on the condition, and then create a custom field constrained on this view (which is just like any table), eg: only show third-parties that are suppliers:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
CREATE VIEW llx_societe_supplier_view AS&lt;br /&gt;
SELECT *&lt;br /&gt;
FROM llx_societe&lt;br /&gt;
WHERE fournisseur=1;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
WARNING: foreign keys (and thus constraints) can&#039;t be used on views with MySQL, and materialized views neither exist, so you can&#039;t use this method with MySQL.&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
* SQL Transformations&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
=== Semi-dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade option is ideal in most cases, and if not enough, you can use the Cascade Custom option and Overloading Functions.&lt;br /&gt;
* View&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
&lt;br /&gt;
=== Dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade Custom and Overload functions&lt;br /&gt;
* Making your own module, which calls the CustomFields class&lt;br /&gt;
&lt;br /&gt;
=== Timed condition ===&lt;br /&gt;
&lt;br /&gt;
* SQL scheduled events&lt;br /&gt;
* Cron job&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
= Overloading functions =&lt;br /&gt;
&lt;br /&gt;
Here are a few concrete use cases of overloading functions to achieve various goals.&lt;br /&gt;
&lt;br /&gt;
== Dynamic list selection based on another one ==&lt;br /&gt;
&lt;br /&gt;
The goal is to show two custom fields on the Third-Party module: one which gives the zone (secteur) of the third-party (Isere, Alpes du sud, Haute-Savoie...) and the other which gives relative to the zone the list of all the ski resorts (station_a) inside the selected zone.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will show how to manually manage this kind of requirements using [[Module_CustomFields#Overloading_functions:_Adding_your_own_management_code_for_custom_fields|overloading functions]], but in latest CustomFields releases, the same solution can be achieved automatically using the [[Module_CustomFields#Cascade|Cascade option]].&lt;br /&gt;
&lt;br /&gt;
We have one table &#039;&#039;&#039;llx_k_station&#039;&#039;&#039; (rowid, station (char50), secteur(char50)) which contains the following: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1,les 2 alpes,isère&lt;br /&gt;
2,chamrousse,isère&lt;br /&gt;
3,alpe d&#039;huez,isère&lt;br /&gt;
4,vars,alpes du sud&lt;br /&gt;
5,risoul,alpes du sud&lt;br /&gt;
etc...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In CustomFields&#039;s admin panel, we create two custom fields:&lt;br /&gt;
&lt;br /&gt;
1- secteur: returns the list of all zones - type DropdownBox: enum(&#039;- Aucun&#039;,&#039;Alpes du Sud&#039;,&#039;export&#039;,&#039;Haute-Savoie&#039;,&#039;Isère&#039;, etc...)&lt;br /&gt;
&lt;br /&gt;
2- station_a: returns the list of all ski resorts - type constraint on &#039;&#039;&#039;llx_k_station&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Here is the code to put in customfields_fields_extend.lib.php that will allow to show only the ski resorts corresponding to the selected zone:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_editview_societe_station_a (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$parameters, &amp;amp;$action, &amp;amp;$user, &amp;amp;$idvar, &amp;amp;$rightok, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
  global $db; // allow to access database functions&lt;br /&gt;
  $sql=&amp;quot;SELECT llx_k_station.station, llx_k_station.rowid FROM llx_societe_customfields INNER JOIN llx_k_station ON llx_societe_customfields.Secteur = llx_k_station.secteur WHERE llx_societe_customfields.fk_societe=&amp;quot;.$object-&amp;gt;id;&lt;br /&gt;
$result=$db-&amp;gt;query($sql);&lt;br /&gt;
 &lt;br /&gt;
  if ($result) {&lt;br /&gt;
    $num = $db-&amp;gt;num_rows($result);&lt;br /&gt;
    $i = 0;&lt;br /&gt;
    $myarray = array();&lt;br /&gt;
    while ($i &amp;lt; $num)&lt;br /&gt;
    {&lt;br /&gt;
      $obj = $db-&amp;gt;fetch_object($result);&lt;br /&gt;
      $myarray []=array(&#039;id&#039;=&amp;gt;$obj-&amp;gt;rowid, &#039;value&#039;=&amp;gt;$obj-&amp;gt;station);&lt;br /&gt;
      $i++;&lt;br /&gt;
    }&lt;br /&gt;
    $value = $myarray; &lt;br /&gt;
    $db-&amp;gt;free($result);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to manub for giving this case.&lt;br /&gt;
&lt;br /&gt;
== Dynamic discount computation per products lines on propales ==&lt;br /&gt;
&lt;br /&gt;
The goal here is to compute the discount for a product line in propales based on a computation on three custom fields.&lt;br /&gt;
&lt;br /&gt;
First, create three custom fields for &amp;quot;Line commercial proposals&amp;quot;: vl, cpl and lc.&lt;br /&gt;
&lt;br /&gt;
Then add the following overloading function at the end of your customfields_fields_extend.lib.php file (read the comments for more informations):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_propaldet_vl (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
&lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $vl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;vl&#039;};&lt;br /&gt;
    $cpl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;cpl&#039;};&lt;br /&gt;
    $lc = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;lc&#039;};&lt;br /&gt;
&lt;br /&gt;
    // Compute the discount&lt;br /&gt;
    $remise_calculee = (100 - $vl) * (100 - $cpl) * (100 - $lc);&lt;br /&gt;
&lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $remise_calculee = price2num($remise_calculee);&lt;br /&gt;
&lt;br /&gt;
    // Assign the new discount into the PropalLine object&lt;br /&gt;
    $object-&amp;gt;remise_percent = $remise_calculee;&lt;br /&gt;
&lt;br /&gt;
    // Update the price (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
&lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
&lt;br /&gt;
    //$q = &amp;quot;UPDATE  &amp;quot;.MAIN_DB_PREFIX.&amp;quot;propaldet SET remise_percent=&amp;quot;.$remise_calculee.&amp;quot; WHERE  rowid=&amp;quot;.$object-&amp;gt;rowid;&lt;br /&gt;
    //$db-&amp;gt;query($q);&lt;br /&gt;
    &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;vl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;cpl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;lc&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Linking Dolibarr objects from two different modules =&lt;br /&gt;
&lt;br /&gt;
Dolibarr sometimes offers the possibility to link two different modules. Eg: when you convert an Intervention card into an Invoice.&lt;br /&gt;
&lt;br /&gt;
In this case, you might want in your templates to fetch the custom fields of the linked object. Eg: you generate a PDF from the Invoice, but you want to get the custom fields from the Intervention that was converted into the Invoice.&lt;br /&gt;
&lt;br /&gt;
The key here is to use the &#039;&#039;&#039;llx_element_element&#039;&#039;&#039; table or the &#039;&#039;&#039;llx_element_*&#039;&#039;&#039; tables (eg: llx_element_contact): this is a standard Dolibarr table that stores all links between objects of two different modules.&lt;br /&gt;
&lt;br /&gt;
Here is the structure:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
rowid | fk_source | sourcetype | fk_target | targettype&lt;br /&gt;
-------------------------------------------------------&lt;br /&gt;
1 | 2 | fichinter | 5 | facture&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can then use a php code like the following to populate your $object with customfields:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
// This will also create and returns us an instance of the CustomFields class, which we will use to manually query the llx_element_element table&lt;br /&gt;
$customfields = customfields_fill_object($object, null, $outputlangs, &#039;facture&#039;, true); // beautified values&lt;br /&gt;
&lt;br /&gt;
// Querying the llx_element_element table manually to get the link between the invoice and the linked intervention card (we want the id of the intervention card)&lt;br /&gt;
// fetchAny($columns, $table, $where=&#039;&#039;, $orderby=&#039;&#039;, $limitby=&#039;&#039;)&lt;br /&gt;
$eltrow = $customfields-&amp;gt;fetchAny(&#039;*&#039;, MAIN_DB_PREFIX.&#039;element_element&#039;, &#039;targettype=&amp;quot;&#039;.$object-&amp;gt;element.&#039;&amp;quot; and fk_target=&#039;.$object-&amp;gt;id.&#039; and sourcetype=&amp;quot;fichinter&amp;quot;&#039;);&lt;br /&gt;
&lt;br /&gt;
// Save the intervention&#039;s id&lt;br /&gt;
$sourceid = $eltrow[0][&#039;fk_source&#039;];&lt;br /&gt;
&lt;br /&gt;
// Crafting a dummy intervention object&lt;br /&gt;
$fromobject = new stdClass(); // Init an empty object&lt;br /&gt;
$fromobject-&amp;gt;id = $sourceid; // We need the id and..&lt;br /&gt;
$fromobject-&amp;gt;table_element = &#039;fichinter&#039;; // the table_element (name of the table)&lt;br /&gt;
&lt;br /&gt;
// We can then fill $object with the intervention custom fields (we store them in a sub property &#039;fichinter&#039; to avoid conflicts with invoice&#039;s custom fields)&lt;br /&gt;
customfields_fill_object($object, $fromobject, $outputlangs, &#039;fichinter&#039;, true);&lt;br /&gt;
&lt;br /&gt;
// Now you can access invoices custom fields using:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;facture-&amp;gt;cf_myfield);&lt;br /&gt;
// And access intervention custom fields with:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;fichinter-&amp;gt;cf_myfield);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to netassopro for the tip.&lt;br /&gt;
&lt;br /&gt;
= Modify the total price of every product/service line with a coefficient (without tampering the discount) =&lt;br /&gt;
&lt;br /&gt;
Here we will go through the creation and management of a custom field called &#039;&#039;&#039;coefficient&#039;&#039;&#039; that will change the total price of each product by modifying it by the coefficient.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will not create a coefficient per product but rather per product&#039;s line. Thus if you want to create a coefficient that never change between invoices but only depending on the product and stays static, please refer to the next chapter below about creating a custom tax per product.&lt;br /&gt;
&lt;br /&gt;
The preliminary and necessary step is to first create a custom field that will be used to modify the total price. We will create one for Client Invoice Lines.&lt;br /&gt;
&lt;br /&gt;
Go to the CustomFields administration panel, then into the Client Invoice Lines tab, then click on the button New Field, name the field &#039;&#039;&#039;coefficient&#039;&#039;&#039;, and set the type to &#039;&#039;&#039;Double&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
When you go to client invoices in Dolibarr, you should then see something similar to this:&lt;br /&gt;
[[File:Cfe1.jpg]]&lt;br /&gt;
You can see that our coefficient field was successfully created.&lt;br /&gt;
&lt;br /&gt;
Then here are two alternative ways of implementing the change of the total price of each product relativery to this coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change total price in database ==&lt;br /&gt;
&lt;br /&gt;
Here we will use a &amp;quot;save&amp;quot; overloading function to change the total price directly in the database, thus we won&#039;t have to modify anything else (eg: nor PDF templating nor ODT) since the total price will be already updated in the database.&lt;br /&gt;
&lt;br /&gt;
Warning: this method will work only with CustomFields =&amp;gt; 3.2.23 since the &amp;quot;save&amp;quot; overloading function did not work for products lines before.&lt;br /&gt;
&lt;br /&gt;
Just add the following overloading function in your customfields_fields_extend.lib.php file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_facturedet_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
 &lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $coefficient = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;coefficient&#039;}; // you could also use $coefficient = $value;&lt;br /&gt;
 &lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $coefficient = price2num($coefficient);&lt;br /&gt;
 &lt;br /&gt;
    // Get the original subprices (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
 &lt;br /&gt;
    // Update the subprices with the coefficient&lt;br /&gt;
    $object-&amp;gt;total_ht = $object-&amp;gt;total_ht * $coefficient; // compute no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva = $object-&amp;gt;total_tva * $coefficient; // compute tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc * $coefficient; // compute tax included price&lt;br /&gt;
 &lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(&#039;&#039;,true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
 &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;coefficient&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should show the correct total price on the Dolibarr&#039;s interface without modifying the unit subprice (P.U. Unit):&lt;br /&gt;
&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
You have nothing to edit in your PDF or ODT template, since the new subprice with applied coefficient will be automatically printed instead of the old one without coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change printed total price per product without tampering the subprice in the database ==&lt;br /&gt;
&lt;br /&gt;
Here is a way to change the printed total price (the one shown on screen and in PDF/ODT) but without changing the total price in the database.&lt;br /&gt;
&lt;br /&gt;
The following sub-chapters are all independent, thus you can only follow the ones that you require, eg: if you only need to change the price in your PDF documents, just create a custom field then jump to the sub-chapter to &#039;&#039;&#039;Implement the change of price in PDF&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in ODT templates ===&lt;br /&gt;
&lt;br /&gt;
There&#039;s no eval implementation currently inside ODT templates, but there is a kind of hook implementation, called [[Create_an_ODT_document_template#Other_personalized_tags|substitution functions]].&lt;br /&gt;
&lt;br /&gt;
You can either create your own module, then create your own substitution function, but for the sake of simplicity in this tutorial, we will directly use the CustomFields substitution function. However, please remember that you will have to redo your changes everytime you will update the CustomFields module, so if you don&#039;t want to do that it would be better if you create your own module and substitution function.&lt;br /&gt;
&lt;br /&gt;
Open the file &#039;&#039;&#039;htdocs/customfields/core/substitutions/functions_customfields.lib.php&#039;&#039;&#039; and go to the function &#039;&#039;&#039;customfields_completesubstitutionarray_lines&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
At the very end of this function, but inside the customfields check:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) {&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
But just before the ending bracket, you should add the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($object-&amp;gt;table_element_line === &#039;facturedet&#039;) { // First check that we are in the right module, we don&#039;t want to use coefficient for other modules if we didn&#039;t define the custom field there&lt;br /&gt;
    $substitutionarray[&#039;line_price_full&#039;] = $substitutionarray[&#039;line_price_ttc&#039;] * $substitutionarray[&#039;cf_coefficient&#039;]; // multiply the total price with our coefficient&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code will add a new tag &#039;&#039;&#039;{line_price_full}&#039;&#039;&#039; that you can then use inside your ODT templates.&lt;br /&gt;
&lt;br /&gt;
Just place this tag in a table in your ODT document, between the table tags ( [!-- BEGIN row.lines --] ... [!-- END row.lines --] ), and this will print your full price.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in PDF templates ===&lt;br /&gt;
&lt;br /&gt;
First, we will create our own PDF template by copying an existing template, and then we will edit it to change the price of items.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Creating your own PDF template by copying an existing template&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Go into the following folder: &#039;&#039;&#039;htdocs/core/modules/facture/doc/&#039;&#039;&#039;, then copy the file &#039;&#039;&#039;pdf_crabe.modules.php&#039;&#039;&#039; and name the new one &#039;&#039;&#039;pdf_crabecoeff.modules.php&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Then open it in your favourite editor, and change the following lines:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures // change pdf_crabe into pdf_crabecoeff&lt;br /&gt;
{&lt;br /&gt;
...&lt;br /&gt;
    function __construct($db)&lt;br /&gt;
    {&lt;br /&gt;
        ...&lt;br /&gt;
	$this-&amp;gt;name = &amp;quot;crabe&amp;quot;; // change &amp;quot;crabe&amp;quot; into &amp;quot;crabecoeff&amp;quot;&lt;br /&gt;
        ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you have your own PDF template!&lt;br /&gt;
&lt;br /&gt;
Now you should &#039;&#039;&#039;enable your PDF template&#039;&#039;&#039; before doing anything else, by going into the Modules Administration panel, and into the Invoice admin page, then click on the ON button at the right of the &#039;&#039;&#039;crabecoeff&#039;&#039;&#039; entry to enable the template.&lt;br /&gt;
&lt;br /&gt;
Now, let&#039;s proceed onto modifying the prices.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Changing the prices automatically in your PDF&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
You can now do pretty much anything you want in your PDF template, but I will give you a mean to automatically change the prices of every products with our coefficient, as well as the total price, without editing too much the template.&lt;br /&gt;
&lt;br /&gt;
First, try to find the following code of line (should be inside the write_file() method):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf=pdf_getInstance($this-&amp;gt;format);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, just copy/paste the following code &#039;&#039;&#039;below&#039;&#039;&#039; pdf_getInstance():&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$this-&amp;gt;customfields = customfields_fill_object($object, null, $outputlangs, null, true); // beautified values&lt;br /&gt;
$this-&amp;gt;customfields_raw = customfields_fill_object($object, null, $outputlangs, &#039;raw&#039;, null); // raw values&lt;br /&gt;
$this-&amp;gt;customfields_lines = customfields_fill_object_lines($object, null, $outputlangs, null, true); // product lines&#039; values&lt;br /&gt;
&lt;br /&gt;
// Resetting every $object price because we will compute them anew&lt;br /&gt;
$object-&amp;gt;total_ht = 0;&lt;br /&gt;
$object-&amp;gt;total_tva = 0;&lt;br /&gt;
$object-&amp;gt;total_ttc = 0;&lt;br /&gt;
// For each line of product/service, we will recompute all the prices&lt;br /&gt;
foreach($object-&amp;gt;lines as $line) {&lt;br /&gt;
    // Get the coefficient custom field&#039;s value for the current line&lt;br /&gt;
    $coeff = $object-&amp;gt;customfields-&amp;gt;lines-&amp;gt;{$line-&amp;gt;rowid}-&amp;gt;cf_coefficient;&lt;br /&gt;
    // Recompute the price of this product/service line&lt;br /&gt;
    $line-&amp;gt;total_ht = $line-&amp;gt;total_ht*$coeff; // compute no tax price&lt;br /&gt;
    $line-&amp;gt;total_tva = $line-&amp;gt;total_tva*$coeff; // compute tax amount&lt;br /&gt;
    $line-&amp;gt;total_ttc = $line-&amp;gt;total_ttc*$coeff; // compute tax included price&lt;br /&gt;
    // Do the sum of all new products/services prices and tva (will be incremented with every product/service line)&lt;br /&gt;
    $object-&amp;gt;total_ht += $line-&amp;gt;total_ht; // global no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva += $line-&amp;gt;total_tva; // global vat/tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc += $line-&amp;gt;total_ttc; // global  tax included price&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you should try to generate your PDF document, and you should get something like this:&lt;br /&gt;
[[File:Cfe3.jpg]]&lt;br /&gt;
&lt;br /&gt;
Note: printing the coefficient as a column in your PDF document is also possible but will not be covered here because PDF templates will vary for every persons depending on your needs.&lt;br /&gt;
For guidelines about how to do this, you can read the documentation on [[Create_a_PDF_document_template|how to create a PDF template]] and [[Module_CustomFields#Implementing_in_PDF_templates|how to use custom fields in PDF templates]].&lt;br /&gt;
&lt;br /&gt;
Hint: you should be looking inside the following conditional block, inside the write_file() method:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
{&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of total price in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
Unluckily there is not (yet) any hooking context to modify how lines of products/services are printed, thus the only thing you can do is directly edit the &#039;&#039;&#039;template file&#039;&#039;&#039; and manually put inside your code to load your custom field and print it the way you want using HTML formatting, for our example:&lt;br /&gt;
&lt;br /&gt;
* For Dolibarr 3.3 and above, edit the file &#039;&#039;&#039;htdocs/core/tpl/objectline_view.tpl.php&#039;&#039;&#039;&lt;br /&gt;
* For Dolibarr 3.1 and 3.2, edit the file &#039;&#039;&#039;htdocs/core/tpl/freeproductline_view.tpl.php&#039;&#039;&#039; AND &#039;&#039;&#039;predefinedproductline_view.tpl.php&#039;&#039;&#039; (apply the same changes to both files)&lt;br /&gt;
&lt;br /&gt;
In the template file you opened, find the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And replace it by:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $object-&amp;gt;table_element_line; // you can use $object-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
&lt;br /&gt;
// Modifying the HT price and printing it on the Dolibarr&#039;s datasheet interface&lt;br /&gt;
// Applying a multiplying/dividing coefficient to an HT price will give a mathematically correct TTC price since the taxes are also a multiplier&lt;br /&gt;
// However, if you want to apply other mathematical operations (like addition or substaction), you should use the total price with $line-&amp;gt;total_ttc&lt;br /&gt;
if (!empty($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
    echo price($line-&amp;gt;total_ht * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient);&lt;br /&gt;
} else {&lt;br /&gt;
    echo price($line-&amp;gt;total_ht);&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should produce something like this:&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Technical note: here we are using customfields_fill_object() instead of customfields_fill_object_lines() (the latter being specifically adapted to lines and thus should theoretically be used here) for performance reasons because the template file we are editing is called in a loop, and customfields_fill_object_lines() should be called outside a loop (because it already does the job that is done by the loop), so here we &#039;tricks&#039; the customfields_fill_object() function to avoid this loop by submitting &#039;facturedet&#039; as a module by itself (when in fact it&#039;s a product lines table, but it works the same way nevertheless). This is a good demonstration of the flexibility of the CustomFields functions and methods.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the coefficient column in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
It&#039;s also possible to show the coefficient in a new column on the Dolibarr interface.&lt;br /&gt;
&lt;br /&gt;
To do that, you need to do two things:&lt;br /&gt;
&lt;br /&gt;
1- Add the column title &amp;quot;Coeff.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
2- Add the generator that will input the coefficient value for each line.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Add the column title &amp;quot;Coeff.&amp;quot;&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
Open in your favourite editor the file /htdocs/core/class/commonobject.class.php and inside the function printObjectLines (note the plural s), find the following lines of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Total HT&lt;br /&gt;
print &#039;&amp;lt;td align=&amp;quot;right&amp;quot; width=&amp;quot;50&amp;quot;&amp;gt;&#039;.$langs-&amp;gt;trans(&#039;TotalHTShort&#039;).&#039;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is what prints the Total HT price column. We will place the Coeff column just before.&lt;br /&gt;
&lt;br /&gt;
So, just above this code, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
dol_include_once(&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element_line);&lt;br /&gt;
$coeff_field = $customfields-&amp;gt;fetchFieldStruct(&#039;coefficient&#039;);&lt;br /&gt;
if (!empty($coeff_field)) {&lt;br /&gt;
    print &#039;&amp;lt;td align=&amp;quot;right&amp;quot; width=&amp;quot;50&amp;quot;&amp;gt;&#039;.$langs-&amp;gt;trans(&#039;Coeff.&#039;).&#039;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will take care of showing the Coeff. column, but only for modules where you created such a custom field (because the file we modified is a common object for all modules in Dolibarr, not just invoices, thus this modification will gracefully skip this custom field if it does not exists for other modules).&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Add the generator&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/tpl/objectline_view.tpl.php and try to find the following block of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php if ($line-&amp;gt;special_code == 3)	{ ?&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo $langs-&amp;gt;trans(&#039;Option&#039;); ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } else { ?&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is what prints the total HT price value for each line.&lt;br /&gt;
&lt;br /&gt;
Just above of this block of code, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $object-&amp;gt;table_element_line; // you can use $this-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
if (isset($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
    &amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient; ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just like for the column title, this code will take care of generating the coefficient value only if the custom field is defined, because this file is also a generic template for all modules of Dolibarr.&lt;br /&gt;
&lt;br /&gt;
At this point, everything is done, you should see the following:&lt;br /&gt;
[[File:Cf_wiki_coeff_column.png]]&lt;br /&gt;
&lt;br /&gt;
= Custom tax per product =&lt;br /&gt;
&lt;br /&gt;
This part will show you how to create a static custom tax per product. Static means that the tax will be stored per product, and not per product&#039;s line, thus you just have to setup the tax once per product and it will always be applied in every invoice you add this product.&lt;br /&gt;
&lt;br /&gt;
To do that, you will need the CustomFields Pro module + ProductsEasyAccess.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
1- Create a product&#039;s custom fields called &amp;quot;custom_tax&amp;quot;. Now, using ProductsEasyAccess, you can access this field wherever you wants, either to show it on the Dolibarr&#039;s interface (see previous chapter), or in a PDF template, which we will describe below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2- Modify your invoice&#039;s PDF template. You first need to load all the products&#039; fields &#039;&#039;before&#039;&#039; the loop over the products&#039; lines. To do this, search for a line like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// This should be placed above the &amp;quot;loop lines&amp;quot; below, add this:&lt;br /&gt;
&lt;br /&gt;
dol_include_once(&#039;/productseasyaccess/lib/productseasyaccess.lib.php&#039;);&lt;br /&gt;
fill_object_with_products_fields($object);&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines, search for this command&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  // Add this just below the for loop, this will ease your php manipulation after.&lt;br /&gt;
  // This will allow you to more easily access the products&#039; fields (this will store the id of each product inside $lineproductid).&lt;br /&gt;
  $line_product_id = $object-&amp;gt;lines[$i]-&amp;gt;fk_product;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4- You can now use your products&#039; fields in your PDF. To access the custom_tax field, just use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And to print it in your PDF:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell(0,3, $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax, 0, &#039;L&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5- Then, if you want to count the custom tax total and add it to your vat total, you can create a counter inside the for loop we cited in the first step:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_custom_tax = 0; // Initialize the custom tax total counter&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  $object-&amp;gt;total_custom_tax += $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then find inside the function **_tableau_tot**, find the following line:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And prepend the total modification like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc + $object-&amp;gt;total_custom_tax; // prepend this to add the custom tax to the total&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Done. This should show your custom tax per product and your total including all custom taxes.&lt;/div&gt;</summary>
		<author><name>Lrq3000</name></author>
	</entry>
	<entry>
		<id>https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33042</id>
		<title>Module CustomFields Cases</title>
		<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33042"/>
		<updated>2015-01-07T16:08:44Z</updated>

		<summary type="html">&lt;p&gt;Lrq3000: /* Implementing the change of total price in the Dolibarr interface */ Updated code to be more generic and work with latest Dolibarr versions&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:CustomFields]]&lt;br /&gt;
{{TemplateDocDevEn}}&lt;br /&gt;
{{TemplateModEN}}&lt;br /&gt;
{{BasculeDevUserEn|&lt;br /&gt;
name=CustomFields|&lt;br /&gt;
num=8500|&lt;br /&gt;
devdoc=[[Module_CustomFields_Dev]]|&lt;br /&gt;
userdoc=[[Module_CustomFields]]|}}&lt;br /&gt;
This page lists a compilation of concrete, practical examples uses of [[Module_CustomFields|CustomFields]] and of its various features.&lt;br /&gt;
&lt;br /&gt;
If you had an experience with CustomFields, you can also add here your case as an example for others to get inspired.&lt;br /&gt;
&lt;br /&gt;
= How to make a generic PDF template listing all custom fields =&lt;br /&gt;
&lt;br /&gt;
To make a generic PDF template listing all custom fields, you just need to take an existing PDF template, then you need to follow these 3 steps:&lt;br /&gt;
&lt;br /&gt;
1/ Edit the class name (required, else it will conflict with the original template).&lt;br /&gt;
&lt;br /&gt;
Eg: if we take the crabe template for invoices, we change:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;crabe&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
into:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_customfields extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;customfields&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2/ Copy this generic function at the end of the PDF:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propale/order/etc... (CustomFields simpler functions will automatically adapt)&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs); // set default PDF font size&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
    // Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
    $customfields = customfields_fill_object($object, null, $outputlangs, null, true);&lt;br /&gt;
&lt;br /&gt;
    // Setting the starting position of the text cursor&lt;br /&gt;
    $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
    $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
    // Printing the customfields&lt;br /&gt;
    foreach ($object-&amp;gt;customfields as $label=&amp;gt;$value) { // $value is already formatted!&lt;br /&gt;
        // Get translated label&lt;br /&gt;
        $translatedlabel = $customfields-&amp;gt;findLabelPDF($label, $outputlangs); // translated label of the customfield (not translated by default in customfields_fill_object() because a field should always be accessible by a base name, whatever the translation is)&lt;br /&gt;
&lt;br /&gt;
        // PDF formatting, placement and printing&lt;br /&gt;
        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
        $pdf-&amp;gt;MultiCell(0,3, $translatedlabel.&#039; ($object-&amp;gt;customfields-&amp;gt;&#039;.$label.&#039;): &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternative function (with a bit less functionalities, but more controllable):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page (used to debug if CustomFields setup is correct)&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propal/product/whatever...&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs);&lt;br /&gt;
&lt;br /&gt;
            if (empty($object-&amp;gt;table_element) or empty($object-&amp;gt;id)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;Current \$object is not compatible with CustomFields, could not find table_element or id.&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
    $customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element);&lt;br /&gt;
&lt;br /&gt;
            // Fetching custom fields records&lt;br /&gt;
            $fields = $customfields-&amp;gt;fetch($object-&amp;gt;id);&lt;br /&gt;
&lt;br /&gt;
            if (!isset($fields)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;No custom field could be found for this object. Please check your configuration (did you create at least one customfield and set a value in the current datasheet?)&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            } else {&lt;br /&gt;
                // Setting the starting position of the text cursor&lt;br /&gt;
                $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
                $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
                // Printing the customfields&lt;br /&gt;
                foreach ($fields as $key=&amp;gt;$field) {&lt;br /&gt;
                        $translatedname = $customfields-&amp;gt;findLabelPDF($key, $outputlangs); // label of the customfield&lt;br /&gt;
                        $value = $customfields-&amp;gt;simpleprintFieldPDF($key, $field, $outputlangs); // value (cleaned and properly formatted) of the customfield&lt;br /&gt;
&lt;br /&gt;
                        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
                        $pdf-&amp;gt;MultiCell(0,3, $translatedname.&#039;: &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
                        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3/ Call the _pagecustomfields function at the right place (generally, after _pagefoot and just before $pdf-&amp;gt;Close()):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Pied de page&lt;br /&gt;
$this-&amp;gt;_pagefoot($pdf,$object,$outputlangs);&lt;br /&gt;
$pdf-&amp;gt;AliasNbPages();&lt;br /&gt;
&lt;br /&gt;
// CustomFields&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) { // if the customfields module is activated...&lt;br /&gt;
        // Add a page with the customfields&lt;br /&gt;
        $pdf-&amp;gt;AddPage();&lt;br /&gt;
        $this-&amp;gt;_pagecustomfields($pdf,$object,$outputlangs);&lt;br /&gt;
        $pagenb++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
$pdf-&amp;gt;Close(); // you should first search for this, you should only find one occurrence&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then go to the module&#039;s admin panel (eg: invoice admin panel) and enable the template you&#039;ve just created.&lt;br /&gt;
&lt;br /&gt;
You can now use it to test if your customfields work.&lt;br /&gt;
&lt;br /&gt;
= Conditions on fields selection =&lt;br /&gt;
&lt;br /&gt;
Sometimes you may want to show a DropdownBox or Constraint (or another multi-choice type), but you may want to show or hide choices based on conditions (eg: only if it&#039;s a prospect, only if this zone was selected, only if this category was selected, etc..).&lt;br /&gt;
&lt;br /&gt;
To know which is the best solution you should choose to implement your condition, you should first know in which category your condition falls in.&lt;br /&gt;
&lt;br /&gt;
== Conditions categories ==&lt;br /&gt;
&lt;br /&gt;
Conditions can be categorized into 4 types:&lt;br /&gt;
&lt;br /&gt;
* static condition: choices are restrained on a condition that never changes (eg: only show third-parties that are suppliers, or lowercase all data on sql insertion, or check that a field is above a certain number, etc.).&lt;br /&gt;
* semi-dynamic condition: choices that are constrained on a static table but based on the value of another field/customfield. This is also commonly called &amp;quot;Cascaded dropdown lists&amp;quot; or &amp;quot;Dynamically linked dropdown lists&amp;quot;.&lt;br /&gt;
* dynamic: everything is dynamic: choices are based on a subfield of a field of the current module, so that there&#039;s no material table and it&#039;s impossible to create a view (because you would need to create one view per id) (eg: show only the contacts attached to the third-party being accessed)&lt;br /&gt;
* timed condition: a condition that is based on time or recurrence (eg: at 5:00 AM delete old records, etc..)&lt;br /&gt;
&lt;br /&gt;
== Conditions solutions ==&lt;br /&gt;
&lt;br /&gt;
Now that you know what kind of condition you want to set, you can choose one of the following solutions depending on your preferences and knowledge.&lt;br /&gt;
&lt;br /&gt;
In any case, you can always use the &#039;&#039;&#039;CustomFields&#039;s overload functions&#039;&#039;&#039; to fit your needs for nearly every type of condition.&lt;br /&gt;
&lt;br /&gt;
=== Static condition ===&lt;br /&gt;
&lt;br /&gt;
* View: create a view on the table based on the condition, and then create a custom field constrained on this view (which is just like any table), eg: only show third-parties that are suppliers:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
CREATE VIEW llx_societe_supplier_view AS&lt;br /&gt;
SELECT *&lt;br /&gt;
FROM llx_societe&lt;br /&gt;
WHERE fournisseur=1;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
WARNING: foreign keys (and thus constraints) can&#039;t be used on views with MySQL, and materialized views neither exist, so you can&#039;t use this method with MySQL.&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
* SQL Transformations&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
=== Semi-dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade option is ideal in most cases, and if not enough, you can use the Cascade Custom option and Overloading Functions.&lt;br /&gt;
* View&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
&lt;br /&gt;
=== Dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade Custom and Overload functions&lt;br /&gt;
* Making your own module, which calls the CustomFields class&lt;br /&gt;
&lt;br /&gt;
=== Timed condition ===&lt;br /&gt;
&lt;br /&gt;
* SQL scheduled events&lt;br /&gt;
* Cron job&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
= Overloading functions =&lt;br /&gt;
&lt;br /&gt;
Here are a few concrete use cases of overloading functions to achieve various goals.&lt;br /&gt;
&lt;br /&gt;
== Dynamic list selection based on another one ==&lt;br /&gt;
&lt;br /&gt;
The goal is to show two custom fields on the Third-Party module: one which gives the zone (secteur) of the third-party (Isere, Alpes du sud, Haute-Savoie...) and the other which gives relative to the zone the list of all the ski resorts (station_a) inside the selected zone.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will show how to manually manage this kind of requirements using [[Module_CustomFields#Overloading_functions:_Adding_your_own_management_code_for_custom_fields|overloading functions]], but in latest CustomFields releases, the same solution can be achieved automatically using the [[Module_CustomFields#Cascade|Cascade option]].&lt;br /&gt;
&lt;br /&gt;
We have one table &#039;&#039;&#039;llx_k_station&#039;&#039;&#039; (rowid, station (char50), secteur(char50)) which contains the following: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1,les 2 alpes,isère&lt;br /&gt;
2,chamrousse,isère&lt;br /&gt;
3,alpe d&#039;huez,isère&lt;br /&gt;
4,vars,alpes du sud&lt;br /&gt;
5,risoul,alpes du sud&lt;br /&gt;
etc...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In CustomFields&#039;s admin panel, we create two custom fields:&lt;br /&gt;
&lt;br /&gt;
1- secteur: returns the list of all zones - type DropdownBox: enum(&#039;- Aucun&#039;,&#039;Alpes du Sud&#039;,&#039;export&#039;,&#039;Haute-Savoie&#039;,&#039;Isère&#039;, etc...)&lt;br /&gt;
&lt;br /&gt;
2- station_a: returns the list of all ski resorts - type constraint on &#039;&#039;&#039;llx_k_station&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Here is the code to put in customfields_fields_extend.lib.php that will allow to show only the ski resorts corresponding to the selected zone:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_editview_societe_station_a (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$parameters, &amp;amp;$action, &amp;amp;$user, &amp;amp;$idvar, &amp;amp;$rightok, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
  global $db; // allow to access database functions&lt;br /&gt;
  $sql=&amp;quot;SELECT llx_k_station.station, llx_k_station.rowid FROM llx_societe_customfields INNER JOIN llx_k_station ON llx_societe_customfields.Secteur = llx_k_station.secteur WHERE llx_societe_customfields.fk_societe=&amp;quot;.$object-&amp;gt;id;&lt;br /&gt;
$result=$db-&amp;gt;query($sql);&lt;br /&gt;
 &lt;br /&gt;
  if ($result) {&lt;br /&gt;
    $num = $db-&amp;gt;num_rows($result);&lt;br /&gt;
    $i = 0;&lt;br /&gt;
    $myarray = array();&lt;br /&gt;
    while ($i &amp;lt; $num)&lt;br /&gt;
    {&lt;br /&gt;
      $obj = $db-&amp;gt;fetch_object($result);&lt;br /&gt;
      $myarray []=array(&#039;id&#039;=&amp;gt;$obj-&amp;gt;rowid, &#039;value&#039;=&amp;gt;$obj-&amp;gt;station);&lt;br /&gt;
      $i++;&lt;br /&gt;
    }&lt;br /&gt;
    $value = $myarray; &lt;br /&gt;
    $db-&amp;gt;free($result);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to manub for giving this case.&lt;br /&gt;
&lt;br /&gt;
== Dynamic discount computation per products lines on propales ==&lt;br /&gt;
&lt;br /&gt;
The goal here is to compute the discount for a product line in propales based on a computation on three custom fields.&lt;br /&gt;
&lt;br /&gt;
First, create three custom fields for &amp;quot;Line commercial proposals&amp;quot;: vl, cpl and lc.&lt;br /&gt;
&lt;br /&gt;
Then add the following overloading function at the end of your customfields_fields_extend.lib.php file (read the comments for more informations):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_propaldet_vl (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
&lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $vl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;vl&#039;};&lt;br /&gt;
    $cpl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;cpl&#039;};&lt;br /&gt;
    $lc = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;lc&#039;};&lt;br /&gt;
&lt;br /&gt;
    // Compute the discount&lt;br /&gt;
    $remise_calculee = (100 - $vl) * (100 - $cpl) * (100 - $lc);&lt;br /&gt;
&lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $remise_calculee = price2num($remise_calculee);&lt;br /&gt;
&lt;br /&gt;
    // Assign the new discount into the PropalLine object&lt;br /&gt;
    $object-&amp;gt;remise_percent = $remise_calculee;&lt;br /&gt;
&lt;br /&gt;
    // Update the price (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
&lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
&lt;br /&gt;
    //$q = &amp;quot;UPDATE  &amp;quot;.MAIN_DB_PREFIX.&amp;quot;propaldet SET remise_percent=&amp;quot;.$remise_calculee.&amp;quot; WHERE  rowid=&amp;quot;.$object-&amp;gt;rowid;&lt;br /&gt;
    //$db-&amp;gt;query($q);&lt;br /&gt;
    &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;vl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;cpl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;lc&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Linking Dolibarr objects from two different modules =&lt;br /&gt;
&lt;br /&gt;
Dolibarr sometimes offers the possibility to link two different modules. Eg: when you convert an Intervention card into an Invoice.&lt;br /&gt;
&lt;br /&gt;
In this case, you might want in your templates to fetch the custom fields of the linked object. Eg: you generate a PDF from the Invoice, but you want to get the custom fields from the Intervention that was converted into the Invoice.&lt;br /&gt;
&lt;br /&gt;
The key here is to use the &#039;&#039;&#039;llx_element_element&#039;&#039;&#039; table or the &#039;&#039;&#039;llx_element_*&#039;&#039;&#039; tables (eg: llx_element_contact): this is a standard Dolibarr table that stores all links between objects of two different modules.&lt;br /&gt;
&lt;br /&gt;
Here is the structure:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
rowid | fk_source | sourcetype | fk_target | targettype&lt;br /&gt;
-------------------------------------------------------&lt;br /&gt;
1 | 2 | fichinter | 5 | facture&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can then use a php code like the following to populate your $object with customfields:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
// This will also create and returns us an instance of the CustomFields class, which we will use to manually query the llx_element_element table&lt;br /&gt;
$customfields = customfields_fill_object($object, null, $outputlangs, &#039;facture&#039;, true); // beautified values&lt;br /&gt;
&lt;br /&gt;
// Querying the llx_element_element table manually to get the link between the invoice and the linked intervention card (we want the id of the intervention card)&lt;br /&gt;
// fetchAny($columns, $table, $where=&#039;&#039;, $orderby=&#039;&#039;, $limitby=&#039;&#039;)&lt;br /&gt;
$eltrow = $customfields-&amp;gt;fetchAny(&#039;*&#039;, MAIN_DB_PREFIX.&#039;element_element&#039;, &#039;targettype=&amp;quot;&#039;.$object-&amp;gt;element.&#039;&amp;quot; and fk_target=&#039;.$object-&amp;gt;id.&#039; and sourcetype=&amp;quot;fichinter&amp;quot;&#039;);&lt;br /&gt;
&lt;br /&gt;
// Save the intervention&#039;s id&lt;br /&gt;
$sourceid = $eltrow[0][&#039;fk_source&#039;];&lt;br /&gt;
&lt;br /&gt;
// Crafting a dummy intervention object&lt;br /&gt;
$fromobject = new stdClass(); // Init an empty object&lt;br /&gt;
$fromobject-&amp;gt;id = $sourceid; // We need the id and..&lt;br /&gt;
$fromobject-&amp;gt;table_element = &#039;fichinter&#039;; // the table_element (name of the table)&lt;br /&gt;
&lt;br /&gt;
// We can then fill $object with the intervention custom fields (we store them in a sub property &#039;fichinter&#039; to avoid conflicts with invoice&#039;s custom fields)&lt;br /&gt;
customfields_fill_object($object, $fromobject, $outputlangs, &#039;fichinter&#039;, true);&lt;br /&gt;
&lt;br /&gt;
// Now you can access invoices custom fields using:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;facture-&amp;gt;cf_myfield);&lt;br /&gt;
// And access intervention custom fields with:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;fichinter-&amp;gt;cf_myfield);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to netassopro for the tip.&lt;br /&gt;
&lt;br /&gt;
= Modify the total price of every product/service line with a coefficient (without tampering the discount) =&lt;br /&gt;
&lt;br /&gt;
Here we will go through the creation and management of a custom field called &#039;&#039;&#039;coefficient&#039;&#039;&#039; that will change the total price of each product by modifying it by the coefficient.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will not create a coefficient per product but rather per product&#039;s line. Thus if you want to create a coefficient that never change between invoices but only depending on the product and stays static, please refer to the next chapter below about creating a custom tax per product.&lt;br /&gt;
&lt;br /&gt;
The preliminary and necessary step is to first create a custom field that will be used to modify the total price. We will create one for Client Invoice Lines.&lt;br /&gt;
&lt;br /&gt;
Go to the CustomFields administration panel, then into the Client Invoice Lines tab, then click on the button New Field, name the field &#039;&#039;&#039;coefficient&#039;&#039;&#039;, and set the type to &#039;&#039;&#039;Double&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
When you go to client invoices in Dolibarr, you should then see something similar to this:&lt;br /&gt;
[[File:Cfe1.jpg]]&lt;br /&gt;
You can see that our coefficient field was successfully created.&lt;br /&gt;
&lt;br /&gt;
Then here are two alternative ways of implementing the change of the total price of each product relativery to this coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change total price in database ==&lt;br /&gt;
&lt;br /&gt;
Here we will use a &amp;quot;save&amp;quot; overloading function to change the total price directly in the database, thus we won&#039;t have to modify anything else (eg: nor PDF templating nor ODT) since the total price will be already updated in the database.&lt;br /&gt;
&lt;br /&gt;
Warning: this method will work only with CustomFields =&amp;gt; 3.2.23 since the &amp;quot;save&amp;quot; overloading function did not work for products lines before.&lt;br /&gt;
&lt;br /&gt;
Just add the following overloading function in your customfields_fields_extend.lib.php file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_facturedet_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
 &lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $coefficient = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;coefficient&#039;}; // you could also use $coefficient = $value;&lt;br /&gt;
 &lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $coefficient = price2num($coefficient);&lt;br /&gt;
 &lt;br /&gt;
    // Get the original subprices (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
 &lt;br /&gt;
    // Update the subprices with the coefficient&lt;br /&gt;
    $object-&amp;gt;total_ht = $object-&amp;gt;total_ht * $coefficient; // compute no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva = $object-&amp;gt;total_tva * $coefficient; // compute tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc * $coefficient; // compute tax included price&lt;br /&gt;
 &lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(&#039;&#039;,true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
 &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;coefficient&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should show the correct total price on the Dolibarr&#039;s interface without modifying the unit subprice (P.U. Unit):&lt;br /&gt;
&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
You have nothing to edit in your PDF or ODT template, since the new subprice with applied coefficient will be automatically printed instead of the old one without coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change printed total price per product without tampering the subprice in the database ==&lt;br /&gt;
&lt;br /&gt;
Here is a way to change the printed total price (the one shown on screen and in PDF/ODT) but without changing the total price in the database.&lt;br /&gt;
&lt;br /&gt;
The following sub-chapters are all independent, thus you can only follow the ones that you require, eg: if you only need to change the price in your PDF documents, just create a custom field then jump to the sub-chapter to &#039;&#039;&#039;Implement the change of price in PDF&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in ODT templates ===&lt;br /&gt;
&lt;br /&gt;
There&#039;s no eval implementation currently inside ODT templates, but there is a kind of hook implementation, called [[Create_an_ODT_document_template#Other_personalized_tags|substitution functions]].&lt;br /&gt;
&lt;br /&gt;
You can either create your own module, then create your own substitution function, but for the sake of simplicity in this tutorial, we will directly use the CustomFields substitution function. However, please remember that you will have to redo your changes everytime you will update the CustomFields module, so if you don&#039;t want to do that it would be better if you create your own module and substitution function.&lt;br /&gt;
&lt;br /&gt;
Open the file &#039;&#039;&#039;htdocs/customfields/core/substitutions/functions_customfields.lib.php&#039;&#039;&#039; and go to the function &#039;&#039;&#039;customfields_completesubstitutionarray_lines&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
At the very end of this function, but inside the customfields check:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) {&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
But just before the ending bracket, you should add the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($object-&amp;gt;table_element_line === &#039;facturedet&#039;) { // First check that we are in the right module, we don&#039;t want to use coefficient for other modules if we didn&#039;t define the custom field there&lt;br /&gt;
    $substitutionarray[&#039;line_price_full&#039;] = $substitutionarray[&#039;line_price_ttc&#039;] * $substitutionarray[&#039;cf_coefficient&#039;]; // multiply the total price with our coefficient&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code will add a new tag &#039;&#039;&#039;{line_price_full}&#039;&#039;&#039; that you can then use inside your ODT templates.&lt;br /&gt;
&lt;br /&gt;
Just place this tag in a table in your ODT document, between the table tags ( [!-- BEGIN row.lines --] ... [!-- END row.lines --] ), and this will print your full price.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in PDF templates ===&lt;br /&gt;
&lt;br /&gt;
First, we will create our own PDF template by copying an existing template, and then we will edit it to change the price of items.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Creating your own PDF template by copying an existing template&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Go into the following folder: &#039;&#039;&#039;htdocs/core/modules/facture/doc/&#039;&#039;&#039;, then copy the file &#039;&#039;&#039;pdf_crabe.modules.php&#039;&#039;&#039; and name the new one &#039;&#039;&#039;pdf_crabecoeff.modules.php&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Then open it in your favourite editor, and change the following lines:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures // change pdf_crabe into pdf_crabecoeff&lt;br /&gt;
{&lt;br /&gt;
...&lt;br /&gt;
    function __construct($db)&lt;br /&gt;
    {&lt;br /&gt;
        ...&lt;br /&gt;
	$this-&amp;gt;name = &amp;quot;crabe&amp;quot;; // change &amp;quot;crabe&amp;quot; into &amp;quot;crabecoeff&amp;quot;&lt;br /&gt;
        ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you have your own PDF template!&lt;br /&gt;
&lt;br /&gt;
Now you should &#039;&#039;&#039;enable your PDF template&#039;&#039;&#039; before doing anything else, by going into the Modules Administration panel, and into the Invoice admin page, then click on the ON button at the right of the &#039;&#039;&#039;crabecoeff&#039;&#039;&#039; entry to enable the template.&lt;br /&gt;
&lt;br /&gt;
Now, let&#039;s proceed onto modifying the prices.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Changing the prices automatically in your PDF&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
You can now do pretty much anything you want in your PDF template, but I will give you a mean to automatically change the prices of every products with our coefficient, as well as the total price, without editing too much the template.&lt;br /&gt;
&lt;br /&gt;
First, try to find the following code of line (should be inside the write_file() method):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf=pdf_getInstance($this-&amp;gt;format);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, just copy/paste the following code &#039;&#039;&#039;below&#039;&#039;&#039; pdf_getInstance():&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$this-&amp;gt;customfields = customfields_fill_object($object, null, $outputlangs, null, true); // beautified values&lt;br /&gt;
$this-&amp;gt;customfields_raw = customfields_fill_object($object, null, $outputlangs, &#039;raw&#039;, null); // raw values&lt;br /&gt;
$this-&amp;gt;customfields_lines = customfields_fill_object_lines($object, null, $outputlangs, null, true); // product lines&#039; values&lt;br /&gt;
&lt;br /&gt;
// Resetting every $object price because we will compute them anew&lt;br /&gt;
$object-&amp;gt;total_ht = 0;&lt;br /&gt;
$object-&amp;gt;total_tva = 0;&lt;br /&gt;
$object-&amp;gt;total_ttc = 0;&lt;br /&gt;
// For each line of product/service, we will recompute all the prices&lt;br /&gt;
foreach($object-&amp;gt;lines as $line) {&lt;br /&gt;
    // Get the coefficient custom field&#039;s value for the current line&lt;br /&gt;
    $coeff = $object-&amp;gt;customfields-&amp;gt;lines-&amp;gt;{$line-&amp;gt;rowid}-&amp;gt;cf_coefficient;&lt;br /&gt;
    // Recompute the price of this product/service line&lt;br /&gt;
    $line-&amp;gt;total_ht = $line-&amp;gt;total_ht*$coeff; // compute no tax price&lt;br /&gt;
    $line-&amp;gt;total_tva = $line-&amp;gt;total_tva*$coeff; // compute tax amount&lt;br /&gt;
    $line-&amp;gt;total_ttc = $line-&amp;gt;total_ttc*$coeff; // compute tax included price&lt;br /&gt;
    // Do the sum of all new products/services prices and tva (will be incremented with every product/service line)&lt;br /&gt;
    $object-&amp;gt;total_ht += $line-&amp;gt;total_ht; // global no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva += $line-&amp;gt;total_tva; // global vat/tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc += $line-&amp;gt;total_ttc; // global  tax included price&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you should try to generate your PDF document, and you should get something like this:&lt;br /&gt;
[[File:Cfe3.jpg]]&lt;br /&gt;
&lt;br /&gt;
Note: printing the coefficient as a column in your PDF document is also possible but will not be covered here because PDF templates will vary for every persons depending on your needs.&lt;br /&gt;
For guidelines about how to do this, you can read the documentation on [[Create_a_PDF_document_template|how to create a PDF template]] and [[Module_CustomFields#Implementing_in_PDF_templates|how to use custom fields in PDF templates]].&lt;br /&gt;
&lt;br /&gt;
Hint: you should be looking inside the following conditional block, inside the write_file() method:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
{&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of total price in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
Unluckily there is not (yet) any hooking context to modify how lines of products/services are printed, thus the only thing you can do is directly edit the &#039;&#039;&#039;template file&#039;&#039;&#039; and manually put inside your code to load your custom field and print it the way you want using HTML formatting, for our example:&lt;br /&gt;
&lt;br /&gt;
* For Dolibarr 3.3 and above, edit the file &#039;&#039;&#039;htdocs/core/tpl/objectline_view.tpl.php&#039;&#039;&#039;&lt;br /&gt;
* For Dolibarr 3.1 and 3.2, edit the file &#039;&#039;&#039;htdocs/core/tpl/freeproductline_view.tpl.php&#039;&#039;&#039; AND &#039;&#039;&#039;predefinedproductline_view.tpl.php&#039;&#039;&#039; (apply the same changes to both files)&lt;br /&gt;
&lt;br /&gt;
In the template file you opened, find the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And replace it by:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $object-&amp;gt;table_element_line; // you can use $object-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
&lt;br /&gt;
// Modifying the HT price and printing it on the Dolibarr&#039;s datasheet interface&lt;br /&gt;
// Applying a multiplying/dividing coefficient to an HT price will give a mathematically correct TTC price since the taxes are also a multiplier&lt;br /&gt;
// However, if you want to apply other mathematical operations (like addition or substaction), you should use the total price with $line-&amp;gt;total_ttc&lt;br /&gt;
if (isset($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
    echo price($line-&amp;gt;total_ht * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient);&lt;br /&gt;
} else {&lt;br /&gt;
    echo price($line-&amp;gt;total_ht);&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should produce something like this:&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Technical note: here we are using customfields_fill_object() instead of customfields_fill_object_lines() (the latter being specifically adapted to lines and thus should theoretically be used here) for performance reasons because the template file we are editing is called in a loop, and customfields_fill_object_lines() should be called outside a loop (because it already does the job that is done by the loop), so here we &#039;tricks&#039; the customfields_fill_object() function to avoid this loop by submitting &#039;facturedet&#039; as a module by itself (when in fact it&#039;s a product lines table, but it works the same way nevertheless). This is a good demonstration of the flexibility of the CustomFields functions and methods.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the coefficient column in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
It&#039;s also possible to show the coefficient in a new column on the Dolibarr interface.&lt;br /&gt;
&lt;br /&gt;
To do that, you need to do two things:&lt;br /&gt;
&lt;br /&gt;
1- Add the column title &amp;quot;Coeff.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
2- Add the generator that will input the coefficient value for each line.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Add the column title &amp;quot;Coeff.&amp;quot;&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
Open in your favourite editor the file /htdocs/core/class/commonobject.class.php and inside the function printObjectLines (note the plural s), find the following lines of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Total HT&lt;br /&gt;
print &#039;&amp;lt;td align=&amp;quot;right&amp;quot; width=&amp;quot;50&amp;quot;&amp;gt;&#039;.$langs-&amp;gt;trans(&#039;TotalHTShort&#039;).&#039;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is what prints the Total HT price column. We will place the Coeff column just before.&lt;br /&gt;
&lt;br /&gt;
So, just above this code, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
dol_include_once(&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element_line);&lt;br /&gt;
$coeff_field = $customfields-&amp;gt;fetchFieldStruct(&#039;coefficient&#039;);&lt;br /&gt;
if (!empty($coeff_field)) {&lt;br /&gt;
    print &#039;&amp;lt;td align=&amp;quot;right&amp;quot; width=&amp;quot;50&amp;quot;&amp;gt;&#039;.$langs-&amp;gt;trans(&#039;Coeff.&#039;).&#039;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will take care of showing the Coeff. column, but only for modules where you created such a custom field (because the file we modified is a common object for all modules in Dolibarr, not just invoices, thus this modification will gracefully skip this custom field if it does not exists for other modules).&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Add the generator&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/tpl/objectline_view.tpl.php and try to find the following block of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php if ($line-&amp;gt;special_code == 3)	{ ?&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo $langs-&amp;gt;trans(&#039;Option&#039;); ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } else { ?&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is what prints the total HT price value for each line.&lt;br /&gt;
&lt;br /&gt;
Just above of this block of code, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $object-&amp;gt;table_element_line; // you can use $this-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
if (isset($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
    &amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient; ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just like for the column title, this code will take care of generating the coefficient value only if the custom field is defined, because this file is also a generic template for all modules of Dolibarr.&lt;br /&gt;
&lt;br /&gt;
At this point, everything is done, you should see the following:&lt;br /&gt;
[[File:Cf_wiki_coeff_column.png]]&lt;br /&gt;
&lt;br /&gt;
= Custom tax per product =&lt;br /&gt;
&lt;br /&gt;
This part will show you how to create a static custom tax per product. Static means that the tax will be stored per product, and not per product&#039;s line, thus you just have to setup the tax once per product and it will always be applied in every invoice you add this product.&lt;br /&gt;
&lt;br /&gt;
To do that, you will need the CustomFields Pro module + ProductsEasyAccess.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
1- Create a product&#039;s custom fields called &amp;quot;custom_tax&amp;quot;. Now, using ProductsEasyAccess, you can access this field wherever you wants, either to show it on the Dolibarr&#039;s interface (see previous chapter), or in a PDF template, which we will describe below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2- Modify your invoice&#039;s PDF template. You first need to load all the products&#039; fields &#039;&#039;before&#039;&#039; the loop over the products&#039; lines. To do this, search for a line like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// This should be placed above the &amp;quot;loop lines&amp;quot; below, add this:&lt;br /&gt;
&lt;br /&gt;
dol_include_once(&#039;/productseasyaccess/lib/productseasyaccess.lib.php&#039;);&lt;br /&gt;
fill_object_with_products_fields($object);&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines, search for this command&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  // Add this just below the for loop, this will ease your php manipulation after.&lt;br /&gt;
  // This will allow you to more easily access the products&#039; fields (this will store the id of each product inside $lineproductid).&lt;br /&gt;
  $line_product_id = $object-&amp;gt;lines[$i]-&amp;gt;fk_product;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4- You can now use your products&#039; fields in your PDF. To access the custom_tax field, just use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And to print it in your PDF:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell(0,3, $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax, 0, &#039;L&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5- Then, if you want to count the custom tax total and add it to your vat total, you can create a counter inside the for loop we cited in the first step:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_custom_tax = 0; // Initialize the custom tax total counter&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  $object-&amp;gt;total_custom_tax += $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then find inside the function **_tableau_tot**, find the following line:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And prepend the total modification like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc + $object-&amp;gt;total_custom_tax; // prepend this to add the custom tax to the total&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Done. This should show your custom tax per product and your total including all custom taxes.&lt;/div&gt;</summary>
		<author><name>Lrq3000</name></author>
	</entry>
	<entry>
		<id>https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33041</id>
		<title>Module CustomFields Cases</title>
		<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33041"/>
		<updated>2015-01-07T15:52:05Z</updated>

		<summary type="html">&lt;p&gt;Lrq3000: /* Implementing the coefficient column in the Dolibarr interface */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:CustomFields]]&lt;br /&gt;
{{TemplateDocDevEn}}&lt;br /&gt;
{{TemplateModEN}}&lt;br /&gt;
{{BasculeDevUserEn|&lt;br /&gt;
name=CustomFields|&lt;br /&gt;
num=8500|&lt;br /&gt;
devdoc=[[Module_CustomFields_Dev]]|&lt;br /&gt;
userdoc=[[Module_CustomFields]]|}}&lt;br /&gt;
This page lists a compilation of concrete, practical examples uses of [[Module_CustomFields|CustomFields]] and of its various features.&lt;br /&gt;
&lt;br /&gt;
If you had an experience with CustomFields, you can also add here your case as an example for others to get inspired.&lt;br /&gt;
&lt;br /&gt;
= How to make a generic PDF template listing all custom fields =&lt;br /&gt;
&lt;br /&gt;
To make a generic PDF template listing all custom fields, you just need to take an existing PDF template, then you need to follow these 3 steps:&lt;br /&gt;
&lt;br /&gt;
1/ Edit the class name (required, else it will conflict with the original template).&lt;br /&gt;
&lt;br /&gt;
Eg: if we take the crabe template for invoices, we change:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;crabe&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
into:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_customfields extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;customfields&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2/ Copy this generic function at the end of the PDF:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propale/order/etc... (CustomFields simpler functions will automatically adapt)&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs); // set default PDF font size&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
    // Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
    $customfields = customfields_fill_object($object, null, $outputlangs, null, true);&lt;br /&gt;
&lt;br /&gt;
    // Setting the starting position of the text cursor&lt;br /&gt;
    $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
    $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
    // Printing the customfields&lt;br /&gt;
    foreach ($object-&amp;gt;customfields as $label=&amp;gt;$value) { // $value is already formatted!&lt;br /&gt;
        // Get translated label&lt;br /&gt;
        $translatedlabel = $customfields-&amp;gt;findLabelPDF($label, $outputlangs); // translated label of the customfield (not translated by default in customfields_fill_object() because a field should always be accessible by a base name, whatever the translation is)&lt;br /&gt;
&lt;br /&gt;
        // PDF formatting, placement and printing&lt;br /&gt;
        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
        $pdf-&amp;gt;MultiCell(0,3, $translatedlabel.&#039; ($object-&amp;gt;customfields-&amp;gt;&#039;.$label.&#039;): &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternative function (with a bit less functionalities, but more controllable):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page (used to debug if CustomFields setup is correct)&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propal/product/whatever...&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs);&lt;br /&gt;
&lt;br /&gt;
            if (empty($object-&amp;gt;table_element) or empty($object-&amp;gt;id)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;Current \$object is not compatible with CustomFields, could not find table_element or id.&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
    $customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element);&lt;br /&gt;
&lt;br /&gt;
            // Fetching custom fields records&lt;br /&gt;
            $fields = $customfields-&amp;gt;fetch($object-&amp;gt;id);&lt;br /&gt;
&lt;br /&gt;
            if (!isset($fields)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;No custom field could be found for this object. Please check your configuration (did you create at least one customfield and set a value in the current datasheet?)&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            } else {&lt;br /&gt;
                // Setting the starting position of the text cursor&lt;br /&gt;
                $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
                $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
                // Printing the customfields&lt;br /&gt;
                foreach ($fields as $key=&amp;gt;$field) {&lt;br /&gt;
                        $translatedname = $customfields-&amp;gt;findLabelPDF($key, $outputlangs); // label of the customfield&lt;br /&gt;
                        $value = $customfields-&amp;gt;simpleprintFieldPDF($key, $field, $outputlangs); // value (cleaned and properly formatted) of the customfield&lt;br /&gt;
&lt;br /&gt;
                        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
                        $pdf-&amp;gt;MultiCell(0,3, $translatedname.&#039;: &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
                        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3/ Call the _pagecustomfields function at the right place (generally, after _pagefoot and just before $pdf-&amp;gt;Close()):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Pied de page&lt;br /&gt;
$this-&amp;gt;_pagefoot($pdf,$object,$outputlangs);&lt;br /&gt;
$pdf-&amp;gt;AliasNbPages();&lt;br /&gt;
&lt;br /&gt;
// CustomFields&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) { // if the customfields module is activated...&lt;br /&gt;
        // Add a page with the customfields&lt;br /&gt;
        $pdf-&amp;gt;AddPage();&lt;br /&gt;
        $this-&amp;gt;_pagecustomfields($pdf,$object,$outputlangs);&lt;br /&gt;
        $pagenb++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
$pdf-&amp;gt;Close(); // you should first search for this, you should only find one occurrence&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then go to the module&#039;s admin panel (eg: invoice admin panel) and enable the template you&#039;ve just created.&lt;br /&gt;
&lt;br /&gt;
You can now use it to test if your customfields work.&lt;br /&gt;
&lt;br /&gt;
= Conditions on fields selection =&lt;br /&gt;
&lt;br /&gt;
Sometimes you may want to show a DropdownBox or Constraint (or another multi-choice type), but you may want to show or hide choices based on conditions (eg: only if it&#039;s a prospect, only if this zone was selected, only if this category was selected, etc..).&lt;br /&gt;
&lt;br /&gt;
To know which is the best solution you should choose to implement your condition, you should first know in which category your condition falls in.&lt;br /&gt;
&lt;br /&gt;
== Conditions categories ==&lt;br /&gt;
&lt;br /&gt;
Conditions can be categorized into 4 types:&lt;br /&gt;
&lt;br /&gt;
* static condition: choices are restrained on a condition that never changes (eg: only show third-parties that are suppliers, or lowercase all data on sql insertion, or check that a field is above a certain number, etc.).&lt;br /&gt;
* semi-dynamic condition: choices that are constrained on a static table but based on the value of another field/customfield. This is also commonly called &amp;quot;Cascaded dropdown lists&amp;quot; or &amp;quot;Dynamically linked dropdown lists&amp;quot;.&lt;br /&gt;
* dynamic: everything is dynamic: choices are based on a subfield of a field of the current module, so that there&#039;s no material table and it&#039;s impossible to create a view (because you would need to create one view per id) (eg: show only the contacts attached to the third-party being accessed)&lt;br /&gt;
* timed condition: a condition that is based on time or recurrence (eg: at 5:00 AM delete old records, etc..)&lt;br /&gt;
&lt;br /&gt;
== Conditions solutions ==&lt;br /&gt;
&lt;br /&gt;
Now that you know what kind of condition you want to set, you can choose one of the following solutions depending on your preferences and knowledge.&lt;br /&gt;
&lt;br /&gt;
In any case, you can always use the &#039;&#039;&#039;CustomFields&#039;s overload functions&#039;&#039;&#039; to fit your needs for nearly every type of condition.&lt;br /&gt;
&lt;br /&gt;
=== Static condition ===&lt;br /&gt;
&lt;br /&gt;
* View: create a view on the table based on the condition, and then create a custom field constrained on this view (which is just like any table), eg: only show third-parties that are suppliers:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
CREATE VIEW llx_societe_supplier_view AS&lt;br /&gt;
SELECT *&lt;br /&gt;
FROM llx_societe&lt;br /&gt;
WHERE fournisseur=1;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
WARNING: foreign keys (and thus constraints) can&#039;t be used on views with MySQL, and materialized views neither exist, so you can&#039;t use this method with MySQL.&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
* SQL Transformations&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
=== Semi-dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade option is ideal in most cases, and if not enough, you can use the Cascade Custom option and Overloading Functions.&lt;br /&gt;
* View&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
&lt;br /&gt;
=== Dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade Custom and Overload functions&lt;br /&gt;
* Making your own module, which calls the CustomFields class&lt;br /&gt;
&lt;br /&gt;
=== Timed condition ===&lt;br /&gt;
&lt;br /&gt;
* SQL scheduled events&lt;br /&gt;
* Cron job&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
= Overloading functions =&lt;br /&gt;
&lt;br /&gt;
Here are a few concrete use cases of overloading functions to achieve various goals.&lt;br /&gt;
&lt;br /&gt;
== Dynamic list selection based on another one ==&lt;br /&gt;
&lt;br /&gt;
The goal is to show two custom fields on the Third-Party module: one which gives the zone (secteur) of the third-party (Isere, Alpes du sud, Haute-Savoie...) and the other which gives relative to the zone the list of all the ski resorts (station_a) inside the selected zone.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will show how to manually manage this kind of requirements using [[Module_CustomFields#Overloading_functions:_Adding_your_own_management_code_for_custom_fields|overloading functions]], but in latest CustomFields releases, the same solution can be achieved automatically using the [[Module_CustomFields#Cascade|Cascade option]].&lt;br /&gt;
&lt;br /&gt;
We have one table &#039;&#039;&#039;llx_k_station&#039;&#039;&#039; (rowid, station (char50), secteur(char50)) which contains the following: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1,les 2 alpes,isère&lt;br /&gt;
2,chamrousse,isère&lt;br /&gt;
3,alpe d&#039;huez,isère&lt;br /&gt;
4,vars,alpes du sud&lt;br /&gt;
5,risoul,alpes du sud&lt;br /&gt;
etc...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In CustomFields&#039;s admin panel, we create two custom fields:&lt;br /&gt;
&lt;br /&gt;
1- secteur: returns the list of all zones - type DropdownBox: enum(&#039;- Aucun&#039;,&#039;Alpes du Sud&#039;,&#039;export&#039;,&#039;Haute-Savoie&#039;,&#039;Isère&#039;, etc...)&lt;br /&gt;
&lt;br /&gt;
2- station_a: returns the list of all ski resorts - type constraint on &#039;&#039;&#039;llx_k_station&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Here is the code to put in customfields_fields_extend.lib.php that will allow to show only the ski resorts corresponding to the selected zone:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_editview_societe_station_a (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$parameters, &amp;amp;$action, &amp;amp;$user, &amp;amp;$idvar, &amp;amp;$rightok, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
  global $db; // allow to access database functions&lt;br /&gt;
  $sql=&amp;quot;SELECT llx_k_station.station, llx_k_station.rowid FROM llx_societe_customfields INNER JOIN llx_k_station ON llx_societe_customfields.Secteur = llx_k_station.secteur WHERE llx_societe_customfields.fk_societe=&amp;quot;.$object-&amp;gt;id;&lt;br /&gt;
$result=$db-&amp;gt;query($sql);&lt;br /&gt;
 &lt;br /&gt;
  if ($result) {&lt;br /&gt;
    $num = $db-&amp;gt;num_rows($result);&lt;br /&gt;
    $i = 0;&lt;br /&gt;
    $myarray = array();&lt;br /&gt;
    while ($i &amp;lt; $num)&lt;br /&gt;
    {&lt;br /&gt;
      $obj = $db-&amp;gt;fetch_object($result);&lt;br /&gt;
      $myarray []=array(&#039;id&#039;=&amp;gt;$obj-&amp;gt;rowid, &#039;value&#039;=&amp;gt;$obj-&amp;gt;station);&lt;br /&gt;
      $i++;&lt;br /&gt;
    }&lt;br /&gt;
    $value = $myarray; &lt;br /&gt;
    $db-&amp;gt;free($result);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to manub for giving this case.&lt;br /&gt;
&lt;br /&gt;
== Dynamic discount computation per products lines on propales ==&lt;br /&gt;
&lt;br /&gt;
The goal here is to compute the discount for a product line in propales based on a computation on three custom fields.&lt;br /&gt;
&lt;br /&gt;
First, create three custom fields for &amp;quot;Line commercial proposals&amp;quot;: vl, cpl and lc.&lt;br /&gt;
&lt;br /&gt;
Then add the following overloading function at the end of your customfields_fields_extend.lib.php file (read the comments for more informations):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_propaldet_vl (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
&lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $vl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;vl&#039;};&lt;br /&gt;
    $cpl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;cpl&#039;};&lt;br /&gt;
    $lc = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;lc&#039;};&lt;br /&gt;
&lt;br /&gt;
    // Compute the discount&lt;br /&gt;
    $remise_calculee = (100 - $vl) * (100 - $cpl) * (100 - $lc);&lt;br /&gt;
&lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $remise_calculee = price2num($remise_calculee);&lt;br /&gt;
&lt;br /&gt;
    // Assign the new discount into the PropalLine object&lt;br /&gt;
    $object-&amp;gt;remise_percent = $remise_calculee;&lt;br /&gt;
&lt;br /&gt;
    // Update the price (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
&lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
&lt;br /&gt;
    //$q = &amp;quot;UPDATE  &amp;quot;.MAIN_DB_PREFIX.&amp;quot;propaldet SET remise_percent=&amp;quot;.$remise_calculee.&amp;quot; WHERE  rowid=&amp;quot;.$object-&amp;gt;rowid;&lt;br /&gt;
    //$db-&amp;gt;query($q);&lt;br /&gt;
    &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;vl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;cpl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;lc&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Linking Dolibarr objects from two different modules =&lt;br /&gt;
&lt;br /&gt;
Dolibarr sometimes offers the possibility to link two different modules. Eg: when you convert an Intervention card into an Invoice.&lt;br /&gt;
&lt;br /&gt;
In this case, you might want in your templates to fetch the custom fields of the linked object. Eg: you generate a PDF from the Invoice, but you want to get the custom fields from the Intervention that was converted into the Invoice.&lt;br /&gt;
&lt;br /&gt;
The key here is to use the &#039;&#039;&#039;llx_element_element&#039;&#039;&#039; table or the &#039;&#039;&#039;llx_element_*&#039;&#039;&#039; tables (eg: llx_element_contact): this is a standard Dolibarr table that stores all links between objects of two different modules.&lt;br /&gt;
&lt;br /&gt;
Here is the structure:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
rowid | fk_source | sourcetype | fk_target | targettype&lt;br /&gt;
-------------------------------------------------------&lt;br /&gt;
1 | 2 | fichinter | 5 | facture&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can then use a php code like the following to populate your $object with customfields:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
// This will also create and returns us an instance of the CustomFields class, which we will use to manually query the llx_element_element table&lt;br /&gt;
$customfields = customfields_fill_object($object, null, $outputlangs, &#039;facture&#039;, true); // beautified values&lt;br /&gt;
&lt;br /&gt;
// Querying the llx_element_element table manually to get the link between the invoice and the linked intervention card (we want the id of the intervention card)&lt;br /&gt;
// fetchAny($columns, $table, $where=&#039;&#039;, $orderby=&#039;&#039;, $limitby=&#039;&#039;)&lt;br /&gt;
$eltrow = $customfields-&amp;gt;fetchAny(&#039;*&#039;, MAIN_DB_PREFIX.&#039;element_element&#039;, &#039;targettype=&amp;quot;&#039;.$object-&amp;gt;element.&#039;&amp;quot; and fk_target=&#039;.$object-&amp;gt;id.&#039; and sourcetype=&amp;quot;fichinter&amp;quot;&#039;);&lt;br /&gt;
&lt;br /&gt;
// Save the intervention&#039;s id&lt;br /&gt;
$sourceid = $eltrow[0][&#039;fk_source&#039;];&lt;br /&gt;
&lt;br /&gt;
// Crafting a dummy intervention object&lt;br /&gt;
$fromobject = new stdClass(); // Init an empty object&lt;br /&gt;
$fromobject-&amp;gt;id = $sourceid; // We need the id and..&lt;br /&gt;
$fromobject-&amp;gt;table_element = &#039;fichinter&#039;; // the table_element (name of the table)&lt;br /&gt;
&lt;br /&gt;
// We can then fill $object with the intervention custom fields (we store them in a sub property &#039;fichinter&#039; to avoid conflicts with invoice&#039;s custom fields)&lt;br /&gt;
customfields_fill_object($object, $fromobject, $outputlangs, &#039;fichinter&#039;, true);&lt;br /&gt;
&lt;br /&gt;
// Now you can access invoices custom fields using:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;facture-&amp;gt;cf_myfield);&lt;br /&gt;
// And access intervention custom fields with:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;fichinter-&amp;gt;cf_myfield);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to netassopro for the tip.&lt;br /&gt;
&lt;br /&gt;
= Modify the total price of every product/service line with a coefficient (without tampering the discount) =&lt;br /&gt;
&lt;br /&gt;
Here we will go through the creation and management of a custom field called &#039;&#039;&#039;coefficient&#039;&#039;&#039; that will change the total price of each product by modifying it by the coefficient.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will not create a coefficient per product but rather per product&#039;s line. Thus if you want to create a coefficient that never change between invoices but only depending on the product and stays static, please refer to the next chapter below about creating a custom tax per product.&lt;br /&gt;
&lt;br /&gt;
The preliminary and necessary step is to first create a custom field that will be used to modify the total price. We will create one for Client Invoice Lines.&lt;br /&gt;
&lt;br /&gt;
Go to the CustomFields administration panel, then into the Client Invoice Lines tab, then click on the button New Field, name the field &#039;&#039;&#039;coefficient&#039;&#039;&#039;, and set the type to &#039;&#039;&#039;Double&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
When you go to client invoices in Dolibarr, you should then see something similar to this:&lt;br /&gt;
[[File:Cfe1.jpg]]&lt;br /&gt;
You can see that our coefficient field was successfully created.&lt;br /&gt;
&lt;br /&gt;
Then here are two alternative ways of implementing the change of the total price of each product relativery to this coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change total price in database ==&lt;br /&gt;
&lt;br /&gt;
Here we will use a &amp;quot;save&amp;quot; overloading function to change the total price directly in the database, thus we won&#039;t have to modify anything else (eg: nor PDF templating nor ODT) since the total price will be already updated in the database.&lt;br /&gt;
&lt;br /&gt;
Warning: this method will work only with CustomFields =&amp;gt; 3.2.23 since the &amp;quot;save&amp;quot; overloading function did not work for products lines before.&lt;br /&gt;
&lt;br /&gt;
Just add the following overloading function in your customfields_fields_extend.lib.php file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_facturedet_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
 &lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $coefficient = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;coefficient&#039;}; // you could also use $coefficient = $value;&lt;br /&gt;
 &lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $coefficient = price2num($coefficient);&lt;br /&gt;
 &lt;br /&gt;
    // Get the original subprices (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
 &lt;br /&gt;
    // Update the subprices with the coefficient&lt;br /&gt;
    $object-&amp;gt;total_ht = $object-&amp;gt;total_ht * $coefficient; // compute no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva = $object-&amp;gt;total_tva * $coefficient; // compute tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc * $coefficient; // compute tax included price&lt;br /&gt;
 &lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(&#039;&#039;,true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
 &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;coefficient&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should show the correct total price on the Dolibarr&#039;s interface without modifying the unit subprice (P.U. Unit):&lt;br /&gt;
&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
You have nothing to edit in your PDF or ODT template, since the new subprice with applied coefficient will be automatically printed instead of the old one without coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change printed total price per product without tampering the subprice in the database ==&lt;br /&gt;
&lt;br /&gt;
Here is a way to change the printed total price (the one shown on screen and in PDF/ODT) but without changing the total price in the database.&lt;br /&gt;
&lt;br /&gt;
The following sub-chapters are all independent, thus you can only follow the ones that you require, eg: if you only need to change the price in your PDF documents, just create a custom field then jump to the sub-chapter to &#039;&#039;&#039;Implement the change of price in PDF&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in ODT templates ===&lt;br /&gt;
&lt;br /&gt;
There&#039;s no eval implementation currently inside ODT templates, but there is a kind of hook implementation, called [[Create_an_ODT_document_template#Other_personalized_tags|substitution functions]].&lt;br /&gt;
&lt;br /&gt;
You can either create your own module, then create your own substitution function, but for the sake of simplicity in this tutorial, we will directly use the CustomFields substitution function. However, please remember that you will have to redo your changes everytime you will update the CustomFields module, so if you don&#039;t want to do that it would be better if you create your own module and substitution function.&lt;br /&gt;
&lt;br /&gt;
Open the file &#039;&#039;&#039;htdocs/customfields/core/substitutions/functions_customfields.lib.php&#039;&#039;&#039; and go to the function &#039;&#039;&#039;customfields_completesubstitutionarray_lines&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
At the very end of this function, but inside the customfields check:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) {&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
But just before the ending bracket, you should add the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($object-&amp;gt;table_element_line === &#039;facturedet&#039;) { // First check that we are in the right module, we don&#039;t want to use coefficient for other modules if we didn&#039;t define the custom field there&lt;br /&gt;
    $substitutionarray[&#039;line_price_full&#039;] = $substitutionarray[&#039;line_price_ttc&#039;] * $substitutionarray[&#039;cf_coefficient&#039;]; // multiply the total price with our coefficient&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code will add a new tag &#039;&#039;&#039;{line_price_full}&#039;&#039;&#039; that you can then use inside your ODT templates.&lt;br /&gt;
&lt;br /&gt;
Just place this tag in a table in your ODT document, between the table tags ( [!-- BEGIN row.lines --] ... [!-- END row.lines --] ), and this will print your full price.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in PDF templates ===&lt;br /&gt;
&lt;br /&gt;
First, we will create our own PDF template by copying an existing template, and then we will edit it to change the price of items.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Creating your own PDF template by copying an existing template&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Go into the following folder: &#039;&#039;&#039;htdocs/core/modules/facture/doc/&#039;&#039;&#039;, then copy the file &#039;&#039;&#039;pdf_crabe.modules.php&#039;&#039;&#039; and name the new one &#039;&#039;&#039;pdf_crabecoeff.modules.php&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Then open it in your favourite editor, and change the following lines:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures // change pdf_crabe into pdf_crabecoeff&lt;br /&gt;
{&lt;br /&gt;
...&lt;br /&gt;
    function __construct($db)&lt;br /&gt;
    {&lt;br /&gt;
        ...&lt;br /&gt;
	$this-&amp;gt;name = &amp;quot;crabe&amp;quot;; // change &amp;quot;crabe&amp;quot; into &amp;quot;crabecoeff&amp;quot;&lt;br /&gt;
        ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you have your own PDF template!&lt;br /&gt;
&lt;br /&gt;
Now you should &#039;&#039;&#039;enable your PDF template&#039;&#039;&#039; before doing anything else, by going into the Modules Administration panel, and into the Invoice admin page, then click on the ON button at the right of the &#039;&#039;&#039;crabecoeff&#039;&#039;&#039; entry to enable the template.&lt;br /&gt;
&lt;br /&gt;
Now, let&#039;s proceed onto modifying the prices.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Changing the prices automatically in your PDF&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
You can now do pretty much anything you want in your PDF template, but I will give you a mean to automatically change the prices of every products with our coefficient, as well as the total price, without editing too much the template.&lt;br /&gt;
&lt;br /&gt;
First, try to find the following code of line (should be inside the write_file() method):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf=pdf_getInstance($this-&amp;gt;format);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, just copy/paste the following code &#039;&#039;&#039;below&#039;&#039;&#039; pdf_getInstance():&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$this-&amp;gt;customfields = customfields_fill_object($object, null, $outputlangs, null, true); // beautified values&lt;br /&gt;
$this-&amp;gt;customfields_raw = customfields_fill_object($object, null, $outputlangs, &#039;raw&#039;, null); // raw values&lt;br /&gt;
$this-&amp;gt;customfields_lines = customfields_fill_object_lines($object, null, $outputlangs, null, true); // product lines&#039; values&lt;br /&gt;
&lt;br /&gt;
// Resetting every $object price because we will compute them anew&lt;br /&gt;
$object-&amp;gt;total_ht = 0;&lt;br /&gt;
$object-&amp;gt;total_tva = 0;&lt;br /&gt;
$object-&amp;gt;total_ttc = 0;&lt;br /&gt;
// For each line of product/service, we will recompute all the prices&lt;br /&gt;
foreach($object-&amp;gt;lines as $line) {&lt;br /&gt;
    // Get the coefficient custom field&#039;s value for the current line&lt;br /&gt;
    $coeff = $object-&amp;gt;customfields-&amp;gt;lines-&amp;gt;{$line-&amp;gt;rowid}-&amp;gt;cf_coefficient;&lt;br /&gt;
    // Recompute the price of this product/service line&lt;br /&gt;
    $line-&amp;gt;total_ht = $line-&amp;gt;total_ht*$coeff; // compute no tax price&lt;br /&gt;
    $line-&amp;gt;total_tva = $line-&amp;gt;total_tva*$coeff; // compute tax amount&lt;br /&gt;
    $line-&amp;gt;total_ttc = $line-&amp;gt;total_ttc*$coeff; // compute tax included price&lt;br /&gt;
    // Do the sum of all new products/services prices and tva (will be incremented with every product/service line)&lt;br /&gt;
    $object-&amp;gt;total_ht += $line-&amp;gt;total_ht; // global no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva += $line-&amp;gt;total_tva; // global vat/tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc += $line-&amp;gt;total_ttc; // global  tax included price&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you should try to generate your PDF document, and you should get something like this:&lt;br /&gt;
[[File:Cfe3.jpg]]&lt;br /&gt;
&lt;br /&gt;
Note: printing the coefficient as a column in your PDF document is also possible but will not be covered here because PDF templates will vary for every persons depending on your needs.&lt;br /&gt;
For guidelines about how to do this, you can read the documentation on [[Create_a_PDF_document_template|how to create a PDF template]] and [[Module_CustomFields#Implementing_in_PDF_templates|how to use custom fields in PDF templates]].&lt;br /&gt;
&lt;br /&gt;
Hint: you should be looking inside the following conditional block, inside the write_file() method:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
{&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of total price in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
Unluckily there is not (yet) any hooking context to modify how lines of products/services are printed, thus the only thing you can do is directly edit the &#039;&#039;&#039;template file&#039;&#039;&#039; and manually put inside your code to load your custom field and print it the way you want using HTML formatting, for our example:&lt;br /&gt;
&lt;br /&gt;
* For Dolibarr 3.3 and above, edit the file &#039;&#039;&#039;htdocs/core/tpl/objectline_view.tpl.php&#039;&#039;&#039;&lt;br /&gt;
* For Dolibarr 3.1 and 3.2, edit the file &#039;&#039;&#039;htdocs/core/tpl/freeproductline_view.tpl.php&#039;&#039;&#039; AND &#039;&#039;&#039;predefinedproductline_view.tpl.php&#039;&#039;&#039; (apply the same changes to both files)&lt;br /&gt;
&lt;br /&gt;
In the template file you opened, find the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And replace it by:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = $line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = &#039;facturedet&#039;; // you can use $this-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
&lt;br /&gt;
// Modifying the HT price and printing it on the Dolibarr&#039;s datasheet interface&lt;br /&gt;
// Applying a multiplying/dividing coefficient to an HT price will give a mathematically correct TTC price since the taxes are also a multiplier&lt;br /&gt;
// However, if you want to apply other mathematical operations (like addition or substaction), you should use the total price with $line-&amp;gt;total_ttc&lt;br /&gt;
echo price($line-&amp;gt;total_ht * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient);&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should produce something like this:&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Technical note: here we are using customfields_fill_object() instead of customfields_fill_object_lines() (the latter being specifically adapted to lines and thus should theoretically be used here) for performance reasons because the template file we are editing is called in a loop, and customfields_fill_object_lines() should be called outside a loop (because it already does the job that is done by the loop), so here we &#039;tricks&#039; the customfields_fill_object() function to avoid this loop by submitting &#039;facturedet&#039; as a module by itself (when in fact it&#039;s a product lines table, but it works the same way nevertheless). This is a good demonstration of the flexibility of the CustomFields functions and methods.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the coefficient column in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
It&#039;s also possible to show the coefficient in a new column on the Dolibarr interface.&lt;br /&gt;
&lt;br /&gt;
To do that, you need to do two things:&lt;br /&gt;
&lt;br /&gt;
1- Add the column title &amp;quot;Coeff.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
2- Add the generator that will input the coefficient value for each line.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Add the column title &amp;quot;Coeff.&amp;quot;&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
Open in your favourite editor the file /htdocs/core/class/commonobject.class.php and inside the function printObjectLines (note the plural s), find the following lines of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Total HT&lt;br /&gt;
print &#039;&amp;lt;td align=&amp;quot;right&amp;quot; width=&amp;quot;50&amp;quot;&amp;gt;&#039;.$langs-&amp;gt;trans(&#039;TotalHTShort&#039;).&#039;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is what prints the Total HT price column. We will place the Coeff column just before.&lt;br /&gt;
&lt;br /&gt;
So, just above this code, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
dol_include_once(&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element_line);&lt;br /&gt;
$coeff_field = $customfields-&amp;gt;fetchFieldStruct(&#039;coefficient&#039;);&lt;br /&gt;
if (!empty($coeff_field)) {&lt;br /&gt;
    print &#039;&amp;lt;td align=&amp;quot;right&amp;quot; width=&amp;quot;50&amp;quot;&amp;gt;&#039;.$langs-&amp;gt;trans(&#039;Coeff.&#039;).&#039;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will take care of showing the Coeff. column, but only for modules where you created such a custom field (because the file we modified is a common object for all modules in Dolibarr, not just invoices, thus this modification will gracefully skip this custom field if it does not exists for other modules).&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Add the generator&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/tpl/objectline_view.tpl.php and try to find the following block of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php if ($line-&amp;gt;special_code == 3)	{ ?&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo $langs-&amp;gt;trans(&#039;Option&#039;); ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } else { ?&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is what prints the total HT price value for each line.&lt;br /&gt;
&lt;br /&gt;
Just above of this block of code, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $object-&amp;gt;table_element_line; // you can use $this-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
if (isset($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
    &amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient; ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just like for the column title, this code will take care of generating the coefficient value only if the custom field is defined, because this file is also a generic template for all modules of Dolibarr.&lt;br /&gt;
&lt;br /&gt;
At this point, everything is done, you should see the following:&lt;br /&gt;
[[File:Cf_wiki_coeff_column.png]]&lt;br /&gt;
&lt;br /&gt;
= Custom tax per product =&lt;br /&gt;
&lt;br /&gt;
This part will show you how to create a static custom tax per product. Static means that the tax will be stored per product, and not per product&#039;s line, thus you just have to setup the tax once per product and it will always be applied in every invoice you add this product.&lt;br /&gt;
&lt;br /&gt;
To do that, you will need the CustomFields Pro module + ProductsEasyAccess.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
1- Create a product&#039;s custom fields called &amp;quot;custom_tax&amp;quot;. Now, using ProductsEasyAccess, you can access this field wherever you wants, either to show it on the Dolibarr&#039;s interface (see previous chapter), or in a PDF template, which we will describe below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2- Modify your invoice&#039;s PDF template. You first need to load all the products&#039; fields &#039;&#039;before&#039;&#039; the loop over the products&#039; lines. To do this, search for a line like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// This should be placed above the &amp;quot;loop lines&amp;quot; below, add this:&lt;br /&gt;
&lt;br /&gt;
dol_include_once(&#039;/productseasyaccess/lib/productseasyaccess.lib.php&#039;);&lt;br /&gt;
fill_object_with_products_fields($object);&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines, search for this command&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  // Add this just below the for loop, this will ease your php manipulation after.&lt;br /&gt;
  // This will allow you to more easily access the products&#039; fields (this will store the id of each product inside $lineproductid).&lt;br /&gt;
  $line_product_id = $object-&amp;gt;lines[$i]-&amp;gt;fk_product;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4- You can now use your products&#039; fields in your PDF. To access the custom_tax field, just use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And to print it in your PDF:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell(0,3, $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax, 0, &#039;L&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5- Then, if you want to count the custom tax total and add it to your vat total, you can create a counter inside the for loop we cited in the first step:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_custom_tax = 0; // Initialize the custom tax total counter&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  $object-&amp;gt;total_custom_tax += $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then find inside the function **_tableau_tot**, find the following line:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And prepend the total modification like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc + $object-&amp;gt;total_custom_tax; // prepend this to add the custom tax to the total&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Done. This should show your custom tax per product and your total including all custom taxes.&lt;/div&gt;</summary>
		<author><name>Lrq3000</name></author>
	</entry>
	<entry>
		<id>https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33040</id>
		<title>Module CustomFields Cases</title>
		<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33040"/>
		<updated>2015-01-07T15:50:59Z</updated>

		<summary type="html">&lt;p&gt;Lrq3000: /* Implementing the coefficient column in the Dolibarr interface */ Adding image&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:CustomFields]]&lt;br /&gt;
{{TemplateDocDevEn}}&lt;br /&gt;
{{TemplateModEN}}&lt;br /&gt;
{{BasculeDevUserEn|&lt;br /&gt;
name=CustomFields|&lt;br /&gt;
num=8500|&lt;br /&gt;
devdoc=[[Module_CustomFields_Dev]]|&lt;br /&gt;
userdoc=[[Module_CustomFields]]|}}&lt;br /&gt;
This page lists a compilation of concrete, practical examples uses of [[Module_CustomFields|CustomFields]] and of its various features.&lt;br /&gt;
&lt;br /&gt;
If you had an experience with CustomFields, you can also add here your case as an example for others to get inspired.&lt;br /&gt;
&lt;br /&gt;
= How to make a generic PDF template listing all custom fields =&lt;br /&gt;
&lt;br /&gt;
To make a generic PDF template listing all custom fields, you just need to take an existing PDF template, then you need to follow these 3 steps:&lt;br /&gt;
&lt;br /&gt;
1/ Edit the class name (required, else it will conflict with the original template).&lt;br /&gt;
&lt;br /&gt;
Eg: if we take the crabe template for invoices, we change:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;crabe&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
into:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_customfields extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;customfields&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2/ Copy this generic function at the end of the PDF:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propale/order/etc... (CustomFields simpler functions will automatically adapt)&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs); // set default PDF font size&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
    // Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
    $customfields = customfields_fill_object($object, null, $outputlangs, null, true);&lt;br /&gt;
&lt;br /&gt;
    // Setting the starting position of the text cursor&lt;br /&gt;
    $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
    $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
    // Printing the customfields&lt;br /&gt;
    foreach ($object-&amp;gt;customfields as $label=&amp;gt;$value) { // $value is already formatted!&lt;br /&gt;
        // Get translated label&lt;br /&gt;
        $translatedlabel = $customfields-&amp;gt;findLabelPDF($label, $outputlangs); // translated label of the customfield (not translated by default in customfields_fill_object() because a field should always be accessible by a base name, whatever the translation is)&lt;br /&gt;
&lt;br /&gt;
        // PDF formatting, placement and printing&lt;br /&gt;
        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
        $pdf-&amp;gt;MultiCell(0,3, $translatedlabel.&#039; ($object-&amp;gt;customfields-&amp;gt;&#039;.$label.&#039;): &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternative function (with a bit less functionalities, but more controllable):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page (used to debug if CustomFields setup is correct)&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propal/product/whatever...&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs);&lt;br /&gt;
&lt;br /&gt;
            if (empty($object-&amp;gt;table_element) or empty($object-&amp;gt;id)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;Current \$object is not compatible with CustomFields, could not find table_element or id.&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
    $customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element);&lt;br /&gt;
&lt;br /&gt;
            // Fetching custom fields records&lt;br /&gt;
            $fields = $customfields-&amp;gt;fetch($object-&amp;gt;id);&lt;br /&gt;
&lt;br /&gt;
            if (!isset($fields)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;No custom field could be found for this object. Please check your configuration (did you create at least one customfield and set a value in the current datasheet?)&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            } else {&lt;br /&gt;
                // Setting the starting position of the text cursor&lt;br /&gt;
                $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
                $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
                // Printing the customfields&lt;br /&gt;
                foreach ($fields as $key=&amp;gt;$field) {&lt;br /&gt;
                        $translatedname = $customfields-&amp;gt;findLabelPDF($key, $outputlangs); // label of the customfield&lt;br /&gt;
                        $value = $customfields-&amp;gt;simpleprintFieldPDF($key, $field, $outputlangs); // value (cleaned and properly formatted) of the customfield&lt;br /&gt;
&lt;br /&gt;
                        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
                        $pdf-&amp;gt;MultiCell(0,3, $translatedname.&#039;: &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
                        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3/ Call the _pagecustomfields function at the right place (generally, after _pagefoot and just before $pdf-&amp;gt;Close()):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Pied de page&lt;br /&gt;
$this-&amp;gt;_pagefoot($pdf,$object,$outputlangs);&lt;br /&gt;
$pdf-&amp;gt;AliasNbPages();&lt;br /&gt;
&lt;br /&gt;
// CustomFields&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) { // if the customfields module is activated...&lt;br /&gt;
        // Add a page with the customfields&lt;br /&gt;
        $pdf-&amp;gt;AddPage();&lt;br /&gt;
        $this-&amp;gt;_pagecustomfields($pdf,$object,$outputlangs);&lt;br /&gt;
        $pagenb++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
$pdf-&amp;gt;Close(); // you should first search for this, you should only find one occurrence&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then go to the module&#039;s admin panel (eg: invoice admin panel) and enable the template you&#039;ve just created.&lt;br /&gt;
&lt;br /&gt;
You can now use it to test if your customfields work.&lt;br /&gt;
&lt;br /&gt;
= Conditions on fields selection =&lt;br /&gt;
&lt;br /&gt;
Sometimes you may want to show a DropdownBox or Constraint (or another multi-choice type), but you may want to show or hide choices based on conditions (eg: only if it&#039;s a prospect, only if this zone was selected, only if this category was selected, etc..).&lt;br /&gt;
&lt;br /&gt;
To know which is the best solution you should choose to implement your condition, you should first know in which category your condition falls in.&lt;br /&gt;
&lt;br /&gt;
== Conditions categories ==&lt;br /&gt;
&lt;br /&gt;
Conditions can be categorized into 4 types:&lt;br /&gt;
&lt;br /&gt;
* static condition: choices are restrained on a condition that never changes (eg: only show third-parties that are suppliers, or lowercase all data on sql insertion, or check that a field is above a certain number, etc.).&lt;br /&gt;
* semi-dynamic condition: choices that are constrained on a static table but based on the value of another field/customfield. This is also commonly called &amp;quot;Cascaded dropdown lists&amp;quot; or &amp;quot;Dynamically linked dropdown lists&amp;quot;.&lt;br /&gt;
* dynamic: everything is dynamic: choices are based on a subfield of a field of the current module, so that there&#039;s no material table and it&#039;s impossible to create a view (because you would need to create one view per id) (eg: show only the contacts attached to the third-party being accessed)&lt;br /&gt;
* timed condition: a condition that is based on time or recurrence (eg: at 5:00 AM delete old records, etc..)&lt;br /&gt;
&lt;br /&gt;
== Conditions solutions ==&lt;br /&gt;
&lt;br /&gt;
Now that you know what kind of condition you want to set, you can choose one of the following solutions depending on your preferences and knowledge.&lt;br /&gt;
&lt;br /&gt;
In any case, you can always use the &#039;&#039;&#039;CustomFields&#039;s overload functions&#039;&#039;&#039; to fit your needs for nearly every type of condition.&lt;br /&gt;
&lt;br /&gt;
=== Static condition ===&lt;br /&gt;
&lt;br /&gt;
* View: create a view on the table based on the condition, and then create a custom field constrained on this view (which is just like any table), eg: only show third-parties that are suppliers:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
CREATE VIEW llx_societe_supplier_view AS&lt;br /&gt;
SELECT *&lt;br /&gt;
FROM llx_societe&lt;br /&gt;
WHERE fournisseur=1;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
WARNING: foreign keys (and thus constraints) can&#039;t be used on views with MySQL, and materialized views neither exist, so you can&#039;t use this method with MySQL.&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
* SQL Transformations&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
=== Semi-dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade option is ideal in most cases, and if not enough, you can use the Cascade Custom option and Overloading Functions.&lt;br /&gt;
* View&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
&lt;br /&gt;
=== Dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade Custom and Overload functions&lt;br /&gt;
* Making your own module, which calls the CustomFields class&lt;br /&gt;
&lt;br /&gt;
=== Timed condition ===&lt;br /&gt;
&lt;br /&gt;
* SQL scheduled events&lt;br /&gt;
* Cron job&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
= Overloading functions =&lt;br /&gt;
&lt;br /&gt;
Here are a few concrete use cases of overloading functions to achieve various goals.&lt;br /&gt;
&lt;br /&gt;
== Dynamic list selection based on another one ==&lt;br /&gt;
&lt;br /&gt;
The goal is to show two custom fields on the Third-Party module: one which gives the zone (secteur) of the third-party (Isere, Alpes du sud, Haute-Savoie...) and the other which gives relative to the zone the list of all the ski resorts (station_a) inside the selected zone.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will show how to manually manage this kind of requirements using [[Module_CustomFields#Overloading_functions:_Adding_your_own_management_code_for_custom_fields|overloading functions]], but in latest CustomFields releases, the same solution can be achieved automatically using the [[Module_CustomFields#Cascade|Cascade option]].&lt;br /&gt;
&lt;br /&gt;
We have one table &#039;&#039;&#039;llx_k_station&#039;&#039;&#039; (rowid, station (char50), secteur(char50)) which contains the following: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1,les 2 alpes,isère&lt;br /&gt;
2,chamrousse,isère&lt;br /&gt;
3,alpe d&#039;huez,isère&lt;br /&gt;
4,vars,alpes du sud&lt;br /&gt;
5,risoul,alpes du sud&lt;br /&gt;
etc...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In CustomFields&#039;s admin panel, we create two custom fields:&lt;br /&gt;
&lt;br /&gt;
1- secteur: returns the list of all zones - type DropdownBox: enum(&#039;- Aucun&#039;,&#039;Alpes du Sud&#039;,&#039;export&#039;,&#039;Haute-Savoie&#039;,&#039;Isère&#039;, etc...)&lt;br /&gt;
&lt;br /&gt;
2- station_a: returns the list of all ski resorts - type constraint on &#039;&#039;&#039;llx_k_station&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Here is the code to put in customfields_fields_extend.lib.php that will allow to show only the ski resorts corresponding to the selected zone:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_editview_societe_station_a (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$parameters, &amp;amp;$action, &amp;amp;$user, &amp;amp;$idvar, &amp;amp;$rightok, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
  global $db; // allow to access database functions&lt;br /&gt;
  $sql=&amp;quot;SELECT llx_k_station.station, llx_k_station.rowid FROM llx_societe_customfields INNER JOIN llx_k_station ON llx_societe_customfields.Secteur = llx_k_station.secteur WHERE llx_societe_customfields.fk_societe=&amp;quot;.$object-&amp;gt;id;&lt;br /&gt;
$result=$db-&amp;gt;query($sql);&lt;br /&gt;
 &lt;br /&gt;
  if ($result) {&lt;br /&gt;
    $num = $db-&amp;gt;num_rows($result);&lt;br /&gt;
    $i = 0;&lt;br /&gt;
    $myarray = array();&lt;br /&gt;
    while ($i &amp;lt; $num)&lt;br /&gt;
    {&lt;br /&gt;
      $obj = $db-&amp;gt;fetch_object($result);&lt;br /&gt;
      $myarray []=array(&#039;id&#039;=&amp;gt;$obj-&amp;gt;rowid, &#039;value&#039;=&amp;gt;$obj-&amp;gt;station);&lt;br /&gt;
      $i++;&lt;br /&gt;
    }&lt;br /&gt;
    $value = $myarray; &lt;br /&gt;
    $db-&amp;gt;free($result);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to manub for giving this case.&lt;br /&gt;
&lt;br /&gt;
== Dynamic discount computation per products lines on propales ==&lt;br /&gt;
&lt;br /&gt;
The goal here is to compute the discount for a product line in propales based on a computation on three custom fields.&lt;br /&gt;
&lt;br /&gt;
First, create three custom fields for &amp;quot;Line commercial proposals&amp;quot;: vl, cpl and lc.&lt;br /&gt;
&lt;br /&gt;
Then add the following overloading function at the end of your customfields_fields_extend.lib.php file (read the comments for more informations):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_propaldet_vl (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
&lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $vl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;vl&#039;};&lt;br /&gt;
    $cpl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;cpl&#039;};&lt;br /&gt;
    $lc = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;lc&#039;};&lt;br /&gt;
&lt;br /&gt;
    // Compute the discount&lt;br /&gt;
    $remise_calculee = (100 - $vl) * (100 - $cpl) * (100 - $lc);&lt;br /&gt;
&lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $remise_calculee = price2num($remise_calculee);&lt;br /&gt;
&lt;br /&gt;
    // Assign the new discount into the PropalLine object&lt;br /&gt;
    $object-&amp;gt;remise_percent = $remise_calculee;&lt;br /&gt;
&lt;br /&gt;
    // Update the price (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
&lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
&lt;br /&gt;
    //$q = &amp;quot;UPDATE  &amp;quot;.MAIN_DB_PREFIX.&amp;quot;propaldet SET remise_percent=&amp;quot;.$remise_calculee.&amp;quot; WHERE  rowid=&amp;quot;.$object-&amp;gt;rowid;&lt;br /&gt;
    //$db-&amp;gt;query($q);&lt;br /&gt;
    &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;vl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;cpl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;lc&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Linking Dolibarr objects from two different modules =&lt;br /&gt;
&lt;br /&gt;
Dolibarr sometimes offers the possibility to link two different modules. Eg: when you convert an Intervention card into an Invoice.&lt;br /&gt;
&lt;br /&gt;
In this case, you might want in your templates to fetch the custom fields of the linked object. Eg: you generate a PDF from the Invoice, but you want to get the custom fields from the Intervention that was converted into the Invoice.&lt;br /&gt;
&lt;br /&gt;
The key here is to use the &#039;&#039;&#039;llx_element_element&#039;&#039;&#039; table or the &#039;&#039;&#039;llx_element_*&#039;&#039;&#039; tables (eg: llx_element_contact): this is a standard Dolibarr table that stores all links between objects of two different modules.&lt;br /&gt;
&lt;br /&gt;
Here is the structure:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
rowid | fk_source | sourcetype | fk_target | targettype&lt;br /&gt;
-------------------------------------------------------&lt;br /&gt;
1 | 2 | fichinter | 5 | facture&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can then use a php code like the following to populate your $object with customfields:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
// This will also create and returns us an instance of the CustomFields class, which we will use to manually query the llx_element_element table&lt;br /&gt;
$customfields = customfields_fill_object($object, null, $outputlangs, &#039;facture&#039;, true); // beautified values&lt;br /&gt;
&lt;br /&gt;
// Querying the llx_element_element table manually to get the link between the invoice and the linked intervention card (we want the id of the intervention card)&lt;br /&gt;
// fetchAny($columns, $table, $where=&#039;&#039;, $orderby=&#039;&#039;, $limitby=&#039;&#039;)&lt;br /&gt;
$eltrow = $customfields-&amp;gt;fetchAny(&#039;*&#039;, MAIN_DB_PREFIX.&#039;element_element&#039;, &#039;targettype=&amp;quot;&#039;.$object-&amp;gt;element.&#039;&amp;quot; and fk_target=&#039;.$object-&amp;gt;id.&#039; and sourcetype=&amp;quot;fichinter&amp;quot;&#039;);&lt;br /&gt;
&lt;br /&gt;
// Save the intervention&#039;s id&lt;br /&gt;
$sourceid = $eltrow[0][&#039;fk_source&#039;];&lt;br /&gt;
&lt;br /&gt;
// Crafting a dummy intervention object&lt;br /&gt;
$fromobject = new stdClass(); // Init an empty object&lt;br /&gt;
$fromobject-&amp;gt;id = $sourceid; // We need the id and..&lt;br /&gt;
$fromobject-&amp;gt;table_element = &#039;fichinter&#039;; // the table_element (name of the table)&lt;br /&gt;
&lt;br /&gt;
// We can then fill $object with the intervention custom fields (we store them in a sub property &#039;fichinter&#039; to avoid conflicts with invoice&#039;s custom fields)&lt;br /&gt;
customfields_fill_object($object, $fromobject, $outputlangs, &#039;fichinter&#039;, true);&lt;br /&gt;
&lt;br /&gt;
// Now you can access invoices custom fields using:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;facture-&amp;gt;cf_myfield);&lt;br /&gt;
// And access intervention custom fields with:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;fichinter-&amp;gt;cf_myfield);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to netassopro for the tip.&lt;br /&gt;
&lt;br /&gt;
= Modify the total price of every product/service line with a coefficient (without tampering the discount) =&lt;br /&gt;
&lt;br /&gt;
Here we will go through the creation and management of a custom field called &#039;&#039;&#039;coefficient&#039;&#039;&#039; that will change the total price of each product by modifying it by the coefficient.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will not create a coefficient per product but rather per product&#039;s line. Thus if you want to create a coefficient that never change between invoices but only depending on the product and stays static, please refer to the next chapter below about creating a custom tax per product.&lt;br /&gt;
&lt;br /&gt;
The preliminary and necessary step is to first create a custom field that will be used to modify the total price. We will create one for Client Invoice Lines.&lt;br /&gt;
&lt;br /&gt;
Go to the CustomFields administration panel, then into the Client Invoice Lines tab, then click on the button New Field, name the field &#039;&#039;&#039;coefficient&#039;&#039;&#039;, and set the type to &#039;&#039;&#039;Double&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
When you go to client invoices in Dolibarr, you should then see something similar to this:&lt;br /&gt;
[[File:Cfe1.jpg]]&lt;br /&gt;
You can see that our coefficient field was successfully created.&lt;br /&gt;
&lt;br /&gt;
Then here are two alternative ways of implementing the change of the total price of each product relativery to this coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change total price in database ==&lt;br /&gt;
&lt;br /&gt;
Here we will use a &amp;quot;save&amp;quot; overloading function to change the total price directly in the database, thus we won&#039;t have to modify anything else (eg: nor PDF templating nor ODT) since the total price will be already updated in the database.&lt;br /&gt;
&lt;br /&gt;
Warning: this method will work only with CustomFields =&amp;gt; 3.2.23 since the &amp;quot;save&amp;quot; overloading function did not work for products lines before.&lt;br /&gt;
&lt;br /&gt;
Just add the following overloading function in your customfields_fields_extend.lib.php file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_facturedet_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
 &lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $coefficient = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;coefficient&#039;}; // you could also use $coefficient = $value;&lt;br /&gt;
 &lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $coefficient = price2num($coefficient);&lt;br /&gt;
 &lt;br /&gt;
    // Get the original subprices (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
 &lt;br /&gt;
    // Update the subprices with the coefficient&lt;br /&gt;
    $object-&amp;gt;total_ht = $object-&amp;gt;total_ht * $coefficient; // compute no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva = $object-&amp;gt;total_tva * $coefficient; // compute tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc * $coefficient; // compute tax included price&lt;br /&gt;
 &lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(&#039;&#039;,true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
 &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;coefficient&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should show the correct total price on the Dolibarr&#039;s interface without modifying the unit subprice (P.U. Unit):&lt;br /&gt;
&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
You have nothing to edit in your PDF or ODT template, since the new subprice with applied coefficient will be automatically printed instead of the old one without coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change printed total price per product without tampering the subprice in the database ==&lt;br /&gt;
&lt;br /&gt;
Here is a way to change the printed total price (the one shown on screen and in PDF/ODT) but without changing the total price in the database.&lt;br /&gt;
&lt;br /&gt;
The following sub-chapters are all independent, thus you can only follow the ones that you require, eg: if you only need to change the price in your PDF documents, just create a custom field then jump to the sub-chapter to &#039;&#039;&#039;Implement the change of price in PDF&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in ODT templates ===&lt;br /&gt;
&lt;br /&gt;
There&#039;s no eval implementation currently inside ODT templates, but there is a kind of hook implementation, called [[Create_an_ODT_document_template#Other_personalized_tags|substitution functions]].&lt;br /&gt;
&lt;br /&gt;
You can either create your own module, then create your own substitution function, but for the sake of simplicity in this tutorial, we will directly use the CustomFields substitution function. However, please remember that you will have to redo your changes everytime you will update the CustomFields module, so if you don&#039;t want to do that it would be better if you create your own module and substitution function.&lt;br /&gt;
&lt;br /&gt;
Open the file &#039;&#039;&#039;htdocs/customfields/core/substitutions/functions_customfields.lib.php&#039;&#039;&#039; and go to the function &#039;&#039;&#039;customfields_completesubstitutionarray_lines&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
At the very end of this function, but inside the customfields check:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) {&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
But just before the ending bracket, you should add the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($object-&amp;gt;table_element_line === &#039;facturedet&#039;) { // First check that we are in the right module, we don&#039;t want to use coefficient for other modules if we didn&#039;t define the custom field there&lt;br /&gt;
    $substitutionarray[&#039;line_price_full&#039;] = $substitutionarray[&#039;line_price_ttc&#039;] * $substitutionarray[&#039;cf_coefficient&#039;]; // multiply the total price with our coefficient&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code will add a new tag &#039;&#039;&#039;{line_price_full}&#039;&#039;&#039; that you can then use inside your ODT templates.&lt;br /&gt;
&lt;br /&gt;
Just place this tag in a table in your ODT document, between the table tags ( [!-- BEGIN row.lines --] ... [!-- END row.lines --] ), and this will print your full price.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in PDF templates ===&lt;br /&gt;
&lt;br /&gt;
First, we will create our own PDF template by copying an existing template, and then we will edit it to change the price of items.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Creating your own PDF template by copying an existing template&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Go into the following folder: &#039;&#039;&#039;htdocs/core/modules/facture/doc/&#039;&#039;&#039;, then copy the file &#039;&#039;&#039;pdf_crabe.modules.php&#039;&#039;&#039; and name the new one &#039;&#039;&#039;pdf_crabecoeff.modules.php&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Then open it in your favourite editor, and change the following lines:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures // change pdf_crabe into pdf_crabecoeff&lt;br /&gt;
{&lt;br /&gt;
...&lt;br /&gt;
    function __construct($db)&lt;br /&gt;
    {&lt;br /&gt;
        ...&lt;br /&gt;
	$this-&amp;gt;name = &amp;quot;crabe&amp;quot;; // change &amp;quot;crabe&amp;quot; into &amp;quot;crabecoeff&amp;quot;&lt;br /&gt;
        ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you have your own PDF template!&lt;br /&gt;
&lt;br /&gt;
Now you should &#039;&#039;&#039;enable your PDF template&#039;&#039;&#039; before doing anything else, by going into the Modules Administration panel, and into the Invoice admin page, then click on the ON button at the right of the &#039;&#039;&#039;crabecoeff&#039;&#039;&#039; entry to enable the template.&lt;br /&gt;
&lt;br /&gt;
Now, let&#039;s proceed onto modifying the prices.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Changing the prices automatically in your PDF&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
You can now do pretty much anything you want in your PDF template, but I will give you a mean to automatically change the prices of every products with our coefficient, as well as the total price, without editing too much the template.&lt;br /&gt;
&lt;br /&gt;
First, try to find the following code of line (should be inside the write_file() method):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf=pdf_getInstance($this-&amp;gt;format);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, just copy/paste the following code &#039;&#039;&#039;below&#039;&#039;&#039; pdf_getInstance():&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$this-&amp;gt;customfields = customfields_fill_object($object, null, $outputlangs, null, true); // beautified values&lt;br /&gt;
$this-&amp;gt;customfields_raw = customfields_fill_object($object, null, $outputlangs, &#039;raw&#039;, null); // raw values&lt;br /&gt;
$this-&amp;gt;customfields_lines = customfields_fill_object_lines($object, null, $outputlangs, null, true); // product lines&#039; values&lt;br /&gt;
&lt;br /&gt;
// Resetting every $object price because we will compute them anew&lt;br /&gt;
$object-&amp;gt;total_ht = 0;&lt;br /&gt;
$object-&amp;gt;total_tva = 0;&lt;br /&gt;
$object-&amp;gt;total_ttc = 0;&lt;br /&gt;
// For each line of product/service, we will recompute all the prices&lt;br /&gt;
foreach($object-&amp;gt;lines as $line) {&lt;br /&gt;
    // Get the coefficient custom field&#039;s value for the current line&lt;br /&gt;
    $coeff = $object-&amp;gt;customfields-&amp;gt;lines-&amp;gt;{$line-&amp;gt;rowid}-&amp;gt;cf_coefficient;&lt;br /&gt;
    // Recompute the price of this product/service line&lt;br /&gt;
    $line-&amp;gt;total_ht = $line-&amp;gt;total_ht*$coeff; // compute no tax price&lt;br /&gt;
    $line-&amp;gt;total_tva = $line-&amp;gt;total_tva*$coeff; // compute tax amount&lt;br /&gt;
    $line-&amp;gt;total_ttc = $line-&amp;gt;total_ttc*$coeff; // compute tax included price&lt;br /&gt;
    // Do the sum of all new products/services prices and tva (will be incremented with every product/service line)&lt;br /&gt;
    $object-&amp;gt;total_ht += $line-&amp;gt;total_ht; // global no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva += $line-&amp;gt;total_tva; // global vat/tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc += $line-&amp;gt;total_ttc; // global  tax included price&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you should try to generate your PDF document, and you should get something like this:&lt;br /&gt;
[[File:Cfe3.jpg]]&lt;br /&gt;
&lt;br /&gt;
Note: printing the coefficient as a column in your PDF document is also possible but will not be covered here because PDF templates will vary for every persons depending on your needs.&lt;br /&gt;
For guidelines about how to do this, you can read the documentation on [[Create_a_PDF_document_template|how to create a PDF template]] and [[Module_CustomFields#Implementing_in_PDF_templates|how to use custom fields in PDF templates]].&lt;br /&gt;
&lt;br /&gt;
Hint: you should be looking inside the following conditional block, inside the write_file() method:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
{&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of total price in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
Unluckily there is not (yet) any hooking context to modify how lines of products/services are printed, thus the only thing you can do is directly edit the &#039;&#039;&#039;template file&#039;&#039;&#039; and manually put inside your code to load your custom field and print it the way you want using HTML formatting, for our example:&lt;br /&gt;
&lt;br /&gt;
* For Dolibarr 3.3 and above, edit the file &#039;&#039;&#039;htdocs/core/tpl/objectline_view.tpl.php&#039;&#039;&#039;&lt;br /&gt;
* For Dolibarr 3.1 and 3.2, edit the file &#039;&#039;&#039;htdocs/core/tpl/freeproductline_view.tpl.php&#039;&#039;&#039; AND &#039;&#039;&#039;predefinedproductline_view.tpl.php&#039;&#039;&#039; (apply the same changes to both files)&lt;br /&gt;
&lt;br /&gt;
In the template file you opened, find the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And replace it by:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = $line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = &#039;facturedet&#039;; // you can use $this-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
&lt;br /&gt;
// Modifying the HT price and printing it on the Dolibarr&#039;s datasheet interface&lt;br /&gt;
// Applying a multiplying/dividing coefficient to an HT price will give a mathematically correct TTC price since the taxes are also a multiplier&lt;br /&gt;
// However, if you want to apply other mathematical operations (like addition or substaction), you should use the total price with $line-&amp;gt;total_ttc&lt;br /&gt;
echo price($line-&amp;gt;total_ht * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient);&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should produce something like this:&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Technical note: here we are using customfields_fill_object() instead of customfields_fill_object_lines() (the latter being specifically adapted to lines and thus should theoretically be used here) for performance reasons because the template file we are editing is called in a loop, and customfields_fill_object_lines() should be called outside a loop (because it already does the job that is done by the loop), so here we &#039;tricks&#039; the customfields_fill_object() function to avoid this loop by submitting &#039;facturedet&#039; as a module by itself (when in fact it&#039;s a product lines table, but it works the same way nevertheless). This is a good demonstration of the flexibility of the CustomFields functions and methods.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the coefficient column in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
It&#039;s also possible to show the coefficient in a new column on the Dolibarr interface.&lt;br /&gt;
&lt;br /&gt;
To do that, you need to do two things:&lt;br /&gt;
&lt;br /&gt;
1- Add the column title &amp;quot;Coeff.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
2- Add the generator that will input the coefficient value for each line.&lt;br /&gt;
&lt;br /&gt;
==== Add the column title &amp;quot;Coeff.&amp;quot; ====&lt;br /&gt;
&lt;br /&gt;
Open in your favourite editor the file /htdocs/core/class/commonobject.class.php and inside the function printObjectLines (note the plural s), find the following lines of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Total HT&lt;br /&gt;
print &#039;&amp;lt;td align=&amp;quot;right&amp;quot; width=&amp;quot;50&amp;quot;&amp;gt;&#039;.$langs-&amp;gt;trans(&#039;TotalHTShort&#039;).&#039;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is what prints the Total HT price column. We will place the Coeff column just before.&lt;br /&gt;
&lt;br /&gt;
So, just above this code, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
dol_include_once(&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element_line);&lt;br /&gt;
$coeff_field = $customfields-&amp;gt;fetchFieldStruct(&#039;coefficient&#039;);&lt;br /&gt;
if (!empty($coeff_field)) {&lt;br /&gt;
    print &#039;&amp;lt;td align=&amp;quot;right&amp;quot; width=&amp;quot;50&amp;quot;&amp;gt;&#039;.$langs-&amp;gt;trans(&#039;Coeff.&#039;).&#039;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will take care of showing the Coeff. column, but only for modules where you created such a custom field (because the file we modified is a common object for all modules in Dolibarr, not just invoices, thus this modification will gracefully skip this custom field if it does not exists for other modules).&lt;br /&gt;
&lt;br /&gt;
==== Add the generator ====&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/tpl/objectline_view.tpl.php and try to find the following block of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php if ($line-&amp;gt;special_code == 3)	{ ?&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo $langs-&amp;gt;trans(&#039;Option&#039;); ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } else { ?&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is what prints the total HT price value for each line.&lt;br /&gt;
&lt;br /&gt;
Just above of this block of code, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $object-&amp;gt;table_element_line; // you can use $this-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
if (isset($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
    &amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient; ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just like for the column title, this code will take care of generating the coefficient value only if the custom field is defined, because this file is also a generic template for all modules of Dolibarr.&lt;br /&gt;
&lt;br /&gt;
At this point, everything is done, you should see the following:&lt;br /&gt;
[[File:Cf_wiki_coeff_column.png]]&lt;br /&gt;
&lt;br /&gt;
= Custom tax per product =&lt;br /&gt;
&lt;br /&gt;
This part will show you how to create a static custom tax per product. Static means that the tax will be stored per product, and not per product&#039;s line, thus you just have to setup the tax once per product and it will always be applied in every invoice you add this product.&lt;br /&gt;
&lt;br /&gt;
To do that, you will need the CustomFields Pro module + ProductsEasyAccess.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
1- Create a product&#039;s custom fields called &amp;quot;custom_tax&amp;quot;. Now, using ProductsEasyAccess, you can access this field wherever you wants, either to show it on the Dolibarr&#039;s interface (see previous chapter), or in a PDF template, which we will describe below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2- Modify your invoice&#039;s PDF template. You first need to load all the products&#039; fields &#039;&#039;before&#039;&#039; the loop over the products&#039; lines. To do this, search for a line like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// This should be placed above the &amp;quot;loop lines&amp;quot; below, add this:&lt;br /&gt;
&lt;br /&gt;
dol_include_once(&#039;/productseasyaccess/lib/productseasyaccess.lib.php&#039;);&lt;br /&gt;
fill_object_with_products_fields($object);&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines, search for this command&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  // Add this just below the for loop, this will ease your php manipulation after.&lt;br /&gt;
  // This will allow you to more easily access the products&#039; fields (this will store the id of each product inside $lineproductid).&lt;br /&gt;
  $line_product_id = $object-&amp;gt;lines[$i]-&amp;gt;fk_product;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4- You can now use your products&#039; fields in your PDF. To access the custom_tax field, just use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And to print it in your PDF:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell(0,3, $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax, 0, &#039;L&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5- Then, if you want to count the custom tax total and add it to your vat total, you can create a counter inside the for loop we cited in the first step:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_custom_tax = 0; // Initialize the custom tax total counter&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  $object-&amp;gt;total_custom_tax += $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then find inside the function **_tableau_tot**, find the following line:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And prepend the total modification like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc + $object-&amp;gt;total_custom_tax; // prepend this to add the custom tax to the total&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Done. This should show your custom tax per product and your total including all custom taxes.&lt;/div&gt;</summary>
		<author><name>Lrq3000</name></author>
	</entry>
	<entry>
		<id>https://wiki.dolibarr.org/index.php?title=File:Cf_wiki_coeff_column.png&amp;diff=33039</id>
		<title>File:Cf wiki coeff column.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php?title=File:Cf_wiki_coeff_column.png&amp;diff=33039"/>
		<updated>2015-01-07T15:50:38Z</updated>

		<summary type="html">&lt;p&gt;Lrq3000: Adding a column for the Coefficient custom field.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Adding a column for the Coefficient custom field.&lt;/div&gt;</summary>
		<author><name>Lrq3000</name></author>
	</entry>
	<entry>
		<id>https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33038</id>
		<title>Module CustomFields Cases</title>
		<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33038"/>
		<updated>2015-01-07T15:48:40Z</updated>

		<summary type="html">&lt;p&gt;Lrq3000: /* Change printed total price per product without tampering the subprice in the database */ Adding subsection about adding a coefficient column&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:CustomFields]]&lt;br /&gt;
{{TemplateDocDevEn}}&lt;br /&gt;
{{TemplateModEN}}&lt;br /&gt;
{{BasculeDevUserEn|&lt;br /&gt;
name=CustomFields|&lt;br /&gt;
num=8500|&lt;br /&gt;
devdoc=[[Module_CustomFields_Dev]]|&lt;br /&gt;
userdoc=[[Module_CustomFields]]|}}&lt;br /&gt;
This page lists a compilation of concrete, practical examples uses of [[Module_CustomFields|CustomFields]] and of its various features.&lt;br /&gt;
&lt;br /&gt;
If you had an experience with CustomFields, you can also add here your case as an example for others to get inspired.&lt;br /&gt;
&lt;br /&gt;
= How to make a generic PDF template listing all custom fields =&lt;br /&gt;
&lt;br /&gt;
To make a generic PDF template listing all custom fields, you just need to take an existing PDF template, then you need to follow these 3 steps:&lt;br /&gt;
&lt;br /&gt;
1/ Edit the class name (required, else it will conflict with the original template).&lt;br /&gt;
&lt;br /&gt;
Eg: if we take the crabe template for invoices, we change:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;crabe&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
into:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_customfields extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;customfields&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2/ Copy this generic function at the end of the PDF:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propale/order/etc... (CustomFields simpler functions will automatically adapt)&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs); // set default PDF font size&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
    // Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
    $customfields = customfields_fill_object($object, null, $outputlangs, null, true);&lt;br /&gt;
&lt;br /&gt;
    // Setting the starting position of the text cursor&lt;br /&gt;
    $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
    $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
    // Printing the customfields&lt;br /&gt;
    foreach ($object-&amp;gt;customfields as $label=&amp;gt;$value) { // $value is already formatted!&lt;br /&gt;
        // Get translated label&lt;br /&gt;
        $translatedlabel = $customfields-&amp;gt;findLabelPDF($label, $outputlangs); // translated label of the customfield (not translated by default in customfields_fill_object() because a field should always be accessible by a base name, whatever the translation is)&lt;br /&gt;
&lt;br /&gt;
        // PDF formatting, placement and printing&lt;br /&gt;
        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
        $pdf-&amp;gt;MultiCell(0,3, $translatedlabel.&#039; ($object-&amp;gt;customfields-&amp;gt;&#039;.$label.&#039;): &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternative function (with a bit less functionalities, but more controllable):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page (used to debug if CustomFields setup is correct)&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propal/product/whatever...&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs);&lt;br /&gt;
&lt;br /&gt;
            if (empty($object-&amp;gt;table_element) or empty($object-&amp;gt;id)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;Current \$object is not compatible with CustomFields, could not find table_element or id.&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
    $customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element);&lt;br /&gt;
&lt;br /&gt;
            // Fetching custom fields records&lt;br /&gt;
            $fields = $customfields-&amp;gt;fetch($object-&amp;gt;id);&lt;br /&gt;
&lt;br /&gt;
            if (!isset($fields)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;No custom field could be found for this object. Please check your configuration (did you create at least one customfield and set a value in the current datasheet?)&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            } else {&lt;br /&gt;
                // Setting the starting position of the text cursor&lt;br /&gt;
                $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
                $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
                // Printing the customfields&lt;br /&gt;
                foreach ($fields as $key=&amp;gt;$field) {&lt;br /&gt;
                        $translatedname = $customfields-&amp;gt;findLabelPDF($key, $outputlangs); // label of the customfield&lt;br /&gt;
                        $value = $customfields-&amp;gt;simpleprintFieldPDF($key, $field, $outputlangs); // value (cleaned and properly formatted) of the customfield&lt;br /&gt;
&lt;br /&gt;
                        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
                        $pdf-&amp;gt;MultiCell(0,3, $translatedname.&#039;: &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
                        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3/ Call the _pagecustomfields function at the right place (generally, after _pagefoot and just before $pdf-&amp;gt;Close()):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Pied de page&lt;br /&gt;
$this-&amp;gt;_pagefoot($pdf,$object,$outputlangs);&lt;br /&gt;
$pdf-&amp;gt;AliasNbPages();&lt;br /&gt;
&lt;br /&gt;
// CustomFields&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) { // if the customfields module is activated...&lt;br /&gt;
        // Add a page with the customfields&lt;br /&gt;
        $pdf-&amp;gt;AddPage();&lt;br /&gt;
        $this-&amp;gt;_pagecustomfields($pdf,$object,$outputlangs);&lt;br /&gt;
        $pagenb++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
$pdf-&amp;gt;Close(); // you should first search for this, you should only find one occurrence&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then go to the module&#039;s admin panel (eg: invoice admin panel) and enable the template you&#039;ve just created.&lt;br /&gt;
&lt;br /&gt;
You can now use it to test if your customfields work.&lt;br /&gt;
&lt;br /&gt;
= Conditions on fields selection =&lt;br /&gt;
&lt;br /&gt;
Sometimes you may want to show a DropdownBox or Constraint (or another multi-choice type), but you may want to show or hide choices based on conditions (eg: only if it&#039;s a prospect, only if this zone was selected, only if this category was selected, etc..).&lt;br /&gt;
&lt;br /&gt;
To know which is the best solution you should choose to implement your condition, you should first know in which category your condition falls in.&lt;br /&gt;
&lt;br /&gt;
== Conditions categories ==&lt;br /&gt;
&lt;br /&gt;
Conditions can be categorized into 4 types:&lt;br /&gt;
&lt;br /&gt;
* static condition: choices are restrained on a condition that never changes (eg: only show third-parties that are suppliers, or lowercase all data on sql insertion, or check that a field is above a certain number, etc.).&lt;br /&gt;
* semi-dynamic condition: choices that are constrained on a static table but based on the value of another field/customfield. This is also commonly called &amp;quot;Cascaded dropdown lists&amp;quot; or &amp;quot;Dynamically linked dropdown lists&amp;quot;.&lt;br /&gt;
* dynamic: everything is dynamic: choices are based on a subfield of a field of the current module, so that there&#039;s no material table and it&#039;s impossible to create a view (because you would need to create one view per id) (eg: show only the contacts attached to the third-party being accessed)&lt;br /&gt;
* timed condition: a condition that is based on time or recurrence (eg: at 5:00 AM delete old records, etc..)&lt;br /&gt;
&lt;br /&gt;
== Conditions solutions ==&lt;br /&gt;
&lt;br /&gt;
Now that you know what kind of condition you want to set, you can choose one of the following solutions depending on your preferences and knowledge.&lt;br /&gt;
&lt;br /&gt;
In any case, you can always use the &#039;&#039;&#039;CustomFields&#039;s overload functions&#039;&#039;&#039; to fit your needs for nearly every type of condition.&lt;br /&gt;
&lt;br /&gt;
=== Static condition ===&lt;br /&gt;
&lt;br /&gt;
* View: create a view on the table based on the condition, and then create a custom field constrained on this view (which is just like any table), eg: only show third-parties that are suppliers:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
CREATE VIEW llx_societe_supplier_view AS&lt;br /&gt;
SELECT *&lt;br /&gt;
FROM llx_societe&lt;br /&gt;
WHERE fournisseur=1;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
WARNING: foreign keys (and thus constraints) can&#039;t be used on views with MySQL, and materialized views neither exist, so you can&#039;t use this method with MySQL.&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
* SQL Transformations&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
=== Semi-dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade option is ideal in most cases, and if not enough, you can use the Cascade Custom option and Overloading Functions.&lt;br /&gt;
* View&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
&lt;br /&gt;
=== Dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade Custom and Overload functions&lt;br /&gt;
* Making your own module, which calls the CustomFields class&lt;br /&gt;
&lt;br /&gt;
=== Timed condition ===&lt;br /&gt;
&lt;br /&gt;
* SQL scheduled events&lt;br /&gt;
* Cron job&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
= Overloading functions =&lt;br /&gt;
&lt;br /&gt;
Here are a few concrete use cases of overloading functions to achieve various goals.&lt;br /&gt;
&lt;br /&gt;
== Dynamic list selection based on another one ==&lt;br /&gt;
&lt;br /&gt;
The goal is to show two custom fields on the Third-Party module: one which gives the zone (secteur) of the third-party (Isere, Alpes du sud, Haute-Savoie...) and the other which gives relative to the zone the list of all the ski resorts (station_a) inside the selected zone.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will show how to manually manage this kind of requirements using [[Module_CustomFields#Overloading_functions:_Adding_your_own_management_code_for_custom_fields|overloading functions]], but in latest CustomFields releases, the same solution can be achieved automatically using the [[Module_CustomFields#Cascade|Cascade option]].&lt;br /&gt;
&lt;br /&gt;
We have one table &#039;&#039;&#039;llx_k_station&#039;&#039;&#039; (rowid, station (char50), secteur(char50)) which contains the following: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1,les 2 alpes,isère&lt;br /&gt;
2,chamrousse,isère&lt;br /&gt;
3,alpe d&#039;huez,isère&lt;br /&gt;
4,vars,alpes du sud&lt;br /&gt;
5,risoul,alpes du sud&lt;br /&gt;
etc...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In CustomFields&#039;s admin panel, we create two custom fields:&lt;br /&gt;
&lt;br /&gt;
1- secteur: returns the list of all zones - type DropdownBox: enum(&#039;- Aucun&#039;,&#039;Alpes du Sud&#039;,&#039;export&#039;,&#039;Haute-Savoie&#039;,&#039;Isère&#039;, etc...)&lt;br /&gt;
&lt;br /&gt;
2- station_a: returns the list of all ski resorts - type constraint on &#039;&#039;&#039;llx_k_station&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Here is the code to put in customfields_fields_extend.lib.php that will allow to show only the ski resorts corresponding to the selected zone:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_editview_societe_station_a (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$parameters, &amp;amp;$action, &amp;amp;$user, &amp;amp;$idvar, &amp;amp;$rightok, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
  global $db; // allow to access database functions&lt;br /&gt;
  $sql=&amp;quot;SELECT llx_k_station.station, llx_k_station.rowid FROM llx_societe_customfields INNER JOIN llx_k_station ON llx_societe_customfields.Secteur = llx_k_station.secteur WHERE llx_societe_customfields.fk_societe=&amp;quot;.$object-&amp;gt;id;&lt;br /&gt;
$result=$db-&amp;gt;query($sql);&lt;br /&gt;
 &lt;br /&gt;
  if ($result) {&lt;br /&gt;
    $num = $db-&amp;gt;num_rows($result);&lt;br /&gt;
    $i = 0;&lt;br /&gt;
    $myarray = array();&lt;br /&gt;
    while ($i &amp;lt; $num)&lt;br /&gt;
    {&lt;br /&gt;
      $obj = $db-&amp;gt;fetch_object($result);&lt;br /&gt;
      $myarray []=array(&#039;id&#039;=&amp;gt;$obj-&amp;gt;rowid, &#039;value&#039;=&amp;gt;$obj-&amp;gt;station);&lt;br /&gt;
      $i++;&lt;br /&gt;
    }&lt;br /&gt;
    $value = $myarray; &lt;br /&gt;
    $db-&amp;gt;free($result);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to manub for giving this case.&lt;br /&gt;
&lt;br /&gt;
== Dynamic discount computation per products lines on propales ==&lt;br /&gt;
&lt;br /&gt;
The goal here is to compute the discount for a product line in propales based on a computation on three custom fields.&lt;br /&gt;
&lt;br /&gt;
First, create three custom fields for &amp;quot;Line commercial proposals&amp;quot;: vl, cpl and lc.&lt;br /&gt;
&lt;br /&gt;
Then add the following overloading function at the end of your customfields_fields_extend.lib.php file (read the comments for more informations):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_propaldet_vl (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
&lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $vl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;vl&#039;};&lt;br /&gt;
    $cpl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;cpl&#039;};&lt;br /&gt;
    $lc = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;lc&#039;};&lt;br /&gt;
&lt;br /&gt;
    // Compute the discount&lt;br /&gt;
    $remise_calculee = (100 - $vl) * (100 - $cpl) * (100 - $lc);&lt;br /&gt;
&lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $remise_calculee = price2num($remise_calculee);&lt;br /&gt;
&lt;br /&gt;
    // Assign the new discount into the PropalLine object&lt;br /&gt;
    $object-&amp;gt;remise_percent = $remise_calculee;&lt;br /&gt;
&lt;br /&gt;
    // Update the price (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
&lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
&lt;br /&gt;
    //$q = &amp;quot;UPDATE  &amp;quot;.MAIN_DB_PREFIX.&amp;quot;propaldet SET remise_percent=&amp;quot;.$remise_calculee.&amp;quot; WHERE  rowid=&amp;quot;.$object-&amp;gt;rowid;&lt;br /&gt;
    //$db-&amp;gt;query($q);&lt;br /&gt;
    &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;vl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;cpl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;lc&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Linking Dolibarr objects from two different modules =&lt;br /&gt;
&lt;br /&gt;
Dolibarr sometimes offers the possibility to link two different modules. Eg: when you convert an Intervention card into an Invoice.&lt;br /&gt;
&lt;br /&gt;
In this case, you might want in your templates to fetch the custom fields of the linked object. Eg: you generate a PDF from the Invoice, but you want to get the custom fields from the Intervention that was converted into the Invoice.&lt;br /&gt;
&lt;br /&gt;
The key here is to use the &#039;&#039;&#039;llx_element_element&#039;&#039;&#039; table or the &#039;&#039;&#039;llx_element_*&#039;&#039;&#039; tables (eg: llx_element_contact): this is a standard Dolibarr table that stores all links between objects of two different modules.&lt;br /&gt;
&lt;br /&gt;
Here is the structure:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
rowid | fk_source | sourcetype | fk_target | targettype&lt;br /&gt;
-------------------------------------------------------&lt;br /&gt;
1 | 2 | fichinter | 5 | facture&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can then use a php code like the following to populate your $object with customfields:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
// This will also create and returns us an instance of the CustomFields class, which we will use to manually query the llx_element_element table&lt;br /&gt;
$customfields = customfields_fill_object($object, null, $outputlangs, &#039;facture&#039;, true); // beautified values&lt;br /&gt;
&lt;br /&gt;
// Querying the llx_element_element table manually to get the link between the invoice and the linked intervention card (we want the id of the intervention card)&lt;br /&gt;
// fetchAny($columns, $table, $where=&#039;&#039;, $orderby=&#039;&#039;, $limitby=&#039;&#039;)&lt;br /&gt;
$eltrow = $customfields-&amp;gt;fetchAny(&#039;*&#039;, MAIN_DB_PREFIX.&#039;element_element&#039;, &#039;targettype=&amp;quot;&#039;.$object-&amp;gt;element.&#039;&amp;quot; and fk_target=&#039;.$object-&amp;gt;id.&#039; and sourcetype=&amp;quot;fichinter&amp;quot;&#039;);&lt;br /&gt;
&lt;br /&gt;
// Save the intervention&#039;s id&lt;br /&gt;
$sourceid = $eltrow[0][&#039;fk_source&#039;];&lt;br /&gt;
&lt;br /&gt;
// Crafting a dummy intervention object&lt;br /&gt;
$fromobject = new stdClass(); // Init an empty object&lt;br /&gt;
$fromobject-&amp;gt;id = $sourceid; // We need the id and..&lt;br /&gt;
$fromobject-&amp;gt;table_element = &#039;fichinter&#039;; // the table_element (name of the table)&lt;br /&gt;
&lt;br /&gt;
// We can then fill $object with the intervention custom fields (we store them in a sub property &#039;fichinter&#039; to avoid conflicts with invoice&#039;s custom fields)&lt;br /&gt;
customfields_fill_object($object, $fromobject, $outputlangs, &#039;fichinter&#039;, true);&lt;br /&gt;
&lt;br /&gt;
// Now you can access invoices custom fields using:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;facture-&amp;gt;cf_myfield);&lt;br /&gt;
// And access intervention custom fields with:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;fichinter-&amp;gt;cf_myfield);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to netassopro for the tip.&lt;br /&gt;
&lt;br /&gt;
= Modify the total price of every product/service line with a coefficient (without tampering the discount) =&lt;br /&gt;
&lt;br /&gt;
Here we will go through the creation and management of a custom field called &#039;&#039;&#039;coefficient&#039;&#039;&#039; that will change the total price of each product by modifying it by the coefficient.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will not create a coefficient per product but rather per product&#039;s line. Thus if you want to create a coefficient that never change between invoices but only depending on the product and stays static, please refer to the next chapter below about creating a custom tax per product.&lt;br /&gt;
&lt;br /&gt;
The preliminary and necessary step is to first create a custom field that will be used to modify the total price. We will create one for Client Invoice Lines.&lt;br /&gt;
&lt;br /&gt;
Go to the CustomFields administration panel, then into the Client Invoice Lines tab, then click on the button New Field, name the field &#039;&#039;&#039;coefficient&#039;&#039;&#039;, and set the type to &#039;&#039;&#039;Double&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
When you go to client invoices in Dolibarr, you should then see something similar to this:&lt;br /&gt;
[[File:Cfe1.jpg]]&lt;br /&gt;
You can see that our coefficient field was successfully created.&lt;br /&gt;
&lt;br /&gt;
Then here are two alternative ways of implementing the change of the total price of each product relativery to this coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change total price in database ==&lt;br /&gt;
&lt;br /&gt;
Here we will use a &amp;quot;save&amp;quot; overloading function to change the total price directly in the database, thus we won&#039;t have to modify anything else (eg: nor PDF templating nor ODT) since the total price will be already updated in the database.&lt;br /&gt;
&lt;br /&gt;
Warning: this method will work only with CustomFields =&amp;gt; 3.2.23 since the &amp;quot;save&amp;quot; overloading function did not work for products lines before.&lt;br /&gt;
&lt;br /&gt;
Just add the following overloading function in your customfields_fields_extend.lib.php file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_facturedet_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
 &lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $coefficient = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;coefficient&#039;}; // you could also use $coefficient = $value;&lt;br /&gt;
 &lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $coefficient = price2num($coefficient);&lt;br /&gt;
 &lt;br /&gt;
    // Get the original subprices (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
 &lt;br /&gt;
    // Update the subprices with the coefficient&lt;br /&gt;
    $object-&amp;gt;total_ht = $object-&amp;gt;total_ht * $coefficient; // compute no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva = $object-&amp;gt;total_tva * $coefficient; // compute tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc * $coefficient; // compute tax included price&lt;br /&gt;
 &lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(&#039;&#039;,true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
 &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;coefficient&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should show the correct total price on the Dolibarr&#039;s interface without modifying the unit subprice (P.U. Unit):&lt;br /&gt;
&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
You have nothing to edit in your PDF or ODT template, since the new subprice with applied coefficient will be automatically printed instead of the old one without coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change printed total price per product without tampering the subprice in the database ==&lt;br /&gt;
&lt;br /&gt;
Here is a way to change the printed total price (the one shown on screen and in PDF/ODT) but without changing the total price in the database.&lt;br /&gt;
&lt;br /&gt;
The following sub-chapters are all independent, thus you can only follow the ones that you require, eg: if you only need to change the price in your PDF documents, just create a custom field then jump to the sub-chapter to &#039;&#039;&#039;Implement the change of price in PDF&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in ODT templates ===&lt;br /&gt;
&lt;br /&gt;
There&#039;s no eval implementation currently inside ODT templates, but there is a kind of hook implementation, called [[Create_an_ODT_document_template#Other_personalized_tags|substitution functions]].&lt;br /&gt;
&lt;br /&gt;
You can either create your own module, then create your own substitution function, but for the sake of simplicity in this tutorial, we will directly use the CustomFields substitution function. However, please remember that you will have to redo your changes everytime you will update the CustomFields module, so if you don&#039;t want to do that it would be better if you create your own module and substitution function.&lt;br /&gt;
&lt;br /&gt;
Open the file &#039;&#039;&#039;htdocs/customfields/core/substitutions/functions_customfields.lib.php&#039;&#039;&#039; and go to the function &#039;&#039;&#039;customfields_completesubstitutionarray_lines&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
At the very end of this function, but inside the customfields check:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) {&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
But just before the ending bracket, you should add the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($object-&amp;gt;table_element_line === &#039;facturedet&#039;) { // First check that we are in the right module, we don&#039;t want to use coefficient for other modules if we didn&#039;t define the custom field there&lt;br /&gt;
    $substitutionarray[&#039;line_price_full&#039;] = $substitutionarray[&#039;line_price_ttc&#039;] * $substitutionarray[&#039;cf_coefficient&#039;]; // multiply the total price with our coefficient&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code will add a new tag &#039;&#039;&#039;{line_price_full}&#039;&#039;&#039; that you can then use inside your ODT templates.&lt;br /&gt;
&lt;br /&gt;
Just place this tag in a table in your ODT document, between the table tags ( [!-- BEGIN row.lines --] ... [!-- END row.lines --] ), and this will print your full price.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in PDF templates ===&lt;br /&gt;
&lt;br /&gt;
First, we will create our own PDF template by copying an existing template, and then we will edit it to change the price of items.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Creating your own PDF template by copying an existing template&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Go into the following folder: &#039;&#039;&#039;htdocs/core/modules/facture/doc/&#039;&#039;&#039;, then copy the file &#039;&#039;&#039;pdf_crabe.modules.php&#039;&#039;&#039; and name the new one &#039;&#039;&#039;pdf_crabecoeff.modules.php&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Then open it in your favourite editor, and change the following lines:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures // change pdf_crabe into pdf_crabecoeff&lt;br /&gt;
{&lt;br /&gt;
...&lt;br /&gt;
    function __construct($db)&lt;br /&gt;
    {&lt;br /&gt;
        ...&lt;br /&gt;
	$this-&amp;gt;name = &amp;quot;crabe&amp;quot;; // change &amp;quot;crabe&amp;quot; into &amp;quot;crabecoeff&amp;quot;&lt;br /&gt;
        ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you have your own PDF template!&lt;br /&gt;
&lt;br /&gt;
Now you should &#039;&#039;&#039;enable your PDF template&#039;&#039;&#039; before doing anything else, by going into the Modules Administration panel, and into the Invoice admin page, then click on the ON button at the right of the &#039;&#039;&#039;crabecoeff&#039;&#039;&#039; entry to enable the template.&lt;br /&gt;
&lt;br /&gt;
Now, let&#039;s proceed onto modifying the prices.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Changing the prices automatically in your PDF&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
You can now do pretty much anything you want in your PDF template, but I will give you a mean to automatically change the prices of every products with our coefficient, as well as the total price, without editing too much the template.&lt;br /&gt;
&lt;br /&gt;
First, try to find the following code of line (should be inside the write_file() method):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf=pdf_getInstance($this-&amp;gt;format);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, just copy/paste the following code &#039;&#039;&#039;below&#039;&#039;&#039; pdf_getInstance():&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$this-&amp;gt;customfields = customfields_fill_object($object, null, $outputlangs, null, true); // beautified values&lt;br /&gt;
$this-&amp;gt;customfields_raw = customfields_fill_object($object, null, $outputlangs, &#039;raw&#039;, null); // raw values&lt;br /&gt;
$this-&amp;gt;customfields_lines = customfields_fill_object_lines($object, null, $outputlangs, null, true); // product lines&#039; values&lt;br /&gt;
&lt;br /&gt;
// Resetting every $object price because we will compute them anew&lt;br /&gt;
$object-&amp;gt;total_ht = 0;&lt;br /&gt;
$object-&amp;gt;total_tva = 0;&lt;br /&gt;
$object-&amp;gt;total_ttc = 0;&lt;br /&gt;
// For each line of product/service, we will recompute all the prices&lt;br /&gt;
foreach($object-&amp;gt;lines as $line) {&lt;br /&gt;
    // Get the coefficient custom field&#039;s value for the current line&lt;br /&gt;
    $coeff = $object-&amp;gt;customfields-&amp;gt;lines-&amp;gt;{$line-&amp;gt;rowid}-&amp;gt;cf_coefficient;&lt;br /&gt;
    // Recompute the price of this product/service line&lt;br /&gt;
    $line-&amp;gt;total_ht = $line-&amp;gt;total_ht*$coeff; // compute no tax price&lt;br /&gt;
    $line-&amp;gt;total_tva = $line-&amp;gt;total_tva*$coeff; // compute tax amount&lt;br /&gt;
    $line-&amp;gt;total_ttc = $line-&amp;gt;total_ttc*$coeff; // compute tax included price&lt;br /&gt;
    // Do the sum of all new products/services prices and tva (will be incremented with every product/service line)&lt;br /&gt;
    $object-&amp;gt;total_ht += $line-&amp;gt;total_ht; // global no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva += $line-&amp;gt;total_tva; // global vat/tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc += $line-&amp;gt;total_ttc; // global  tax included price&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you should try to generate your PDF document, and you should get something like this:&lt;br /&gt;
[[File:Cfe3.jpg]]&lt;br /&gt;
&lt;br /&gt;
Note: printing the coefficient as a column in your PDF document is also possible but will not be covered here because PDF templates will vary for every persons depending on your needs.&lt;br /&gt;
For guidelines about how to do this, you can read the documentation on [[Create_a_PDF_document_template|how to create a PDF template]] and [[Module_CustomFields#Implementing_in_PDF_templates|how to use custom fields in PDF templates]].&lt;br /&gt;
&lt;br /&gt;
Hint: you should be looking inside the following conditional block, inside the write_file() method:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
{&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of total price in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
Unluckily there is not (yet) any hooking context to modify how lines of products/services are printed, thus the only thing you can do is directly edit the &#039;&#039;&#039;template file&#039;&#039;&#039; and manually put inside your code to load your custom field and print it the way you want using HTML formatting, for our example:&lt;br /&gt;
&lt;br /&gt;
* For Dolibarr 3.3 and above, edit the file &#039;&#039;&#039;htdocs/core/tpl/objectline_view.tpl.php&#039;&#039;&#039;&lt;br /&gt;
* For Dolibarr 3.1 and 3.2, edit the file &#039;&#039;&#039;htdocs/core/tpl/freeproductline_view.tpl.php&#039;&#039;&#039; AND &#039;&#039;&#039;predefinedproductline_view.tpl.php&#039;&#039;&#039; (apply the same changes to both files)&lt;br /&gt;
&lt;br /&gt;
In the template file you opened, find the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And replace it by:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = $line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = &#039;facturedet&#039;; // you can use $this-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
&lt;br /&gt;
// Modifying the HT price and printing it on the Dolibarr&#039;s datasheet interface&lt;br /&gt;
// Applying a multiplying/dividing coefficient to an HT price will give a mathematically correct TTC price since the taxes are also a multiplier&lt;br /&gt;
// However, if you want to apply other mathematical operations (like addition or substaction), you should use the total price with $line-&amp;gt;total_ttc&lt;br /&gt;
echo price($line-&amp;gt;total_ht * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient);&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should produce something like this:&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Technical note: here we are using customfields_fill_object() instead of customfields_fill_object_lines() (the latter being specifically adapted to lines and thus should theoretically be used here) for performance reasons because the template file we are editing is called in a loop, and customfields_fill_object_lines() should be called outside a loop (because it already does the job that is done by the loop), so here we &#039;tricks&#039; the customfields_fill_object() function to avoid this loop by submitting &#039;facturedet&#039; as a module by itself (when in fact it&#039;s a product lines table, but it works the same way nevertheless). This is a good demonstration of the flexibility of the CustomFields functions and methods.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the coefficient column in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
It&#039;s also possible to show the coefficient in a new column on the Dolibarr interface.&lt;br /&gt;
&lt;br /&gt;
To do that, you need to do two things:&lt;br /&gt;
&lt;br /&gt;
1- Add the column title &amp;quot;Coeff.&amp;quot;&lt;br /&gt;
2- Add the generator that will input the coefficient value for each line.&lt;br /&gt;
&lt;br /&gt;
==== Add the column title &amp;quot;Coeff.&amp;quot; ====&lt;br /&gt;
&lt;br /&gt;
Open in your favourite editor the file /htdocs/core/class/commonobject.class.php and inside the function printObjectLines (note the plural s), find the following lines of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Total HT&lt;br /&gt;
print &#039;&amp;lt;td align=&amp;quot;right&amp;quot; width=&amp;quot;50&amp;quot;&amp;gt;&#039;.$langs-&amp;gt;trans(&#039;TotalHTShort&#039;).&#039;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is what prints the Total HT price column. We will place the Coeff column just before.&lt;br /&gt;
&lt;br /&gt;
So, just above this code, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
dol_include_once(&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
$customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element_line);&lt;br /&gt;
$coeff_field = $customfields-&amp;gt;fetchFieldStruct(&#039;coefficient&#039;);&lt;br /&gt;
if (!empty($coeff_field)) {&lt;br /&gt;
    print &#039;&amp;lt;td align=&amp;quot;right&amp;quot; width=&amp;quot;50&amp;quot;&amp;gt;&#039;.$langs-&amp;gt;trans(&#039;Coeff.&#039;).&#039;&amp;lt;/td&amp;gt;&#039;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This will take care of showing the Coeff. column, but only for modules where you created such a custom field (because the file we modified is a common object for all modules in Dolibarr, not just invoices, thus this modification will gracefully skip this custom field if it does not exists for other modules).&lt;br /&gt;
&lt;br /&gt;
==== Add the generator ====&lt;br /&gt;
&lt;br /&gt;
Open the file /htdocs/core/tpl/objectline_view.tpl.php and try to find the following block of code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php if ($line-&amp;gt;special_code == 3)	{ ?&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo $langs-&amp;gt;trans(&#039;Option&#039;); ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } else { ?&amp;gt;&lt;br /&gt;
&amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is what prints the total HT price value for each line.&lt;br /&gt;
&lt;br /&gt;
Just above of this block of code, add the following:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Coefficient (CustomFields)&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = (!empty($line-&amp;gt;id))?$line-&amp;gt;id:$line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = $object-&amp;gt;table_element_line; // you can use $this-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
if (isset($linecf-&amp;gt;customfields-&amp;gt;cf_coefficient)) {&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
    &amp;lt;td align=&amp;quot;right&amp;quot; class=&amp;quot;nowrap&amp;quot;&amp;gt;&amp;lt;?php $coldisplay++; ?&amp;gt;&amp;lt;?php echo $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient; ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
&amp;lt;?php } ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just like for the column title, this code will take care of generating the coefficient value only if the custom field is defined, because this file is also a generic template for all modules of Dolibarr.&lt;br /&gt;
&lt;br /&gt;
At this point, everything is done, you should see the following:&lt;br /&gt;
&lt;br /&gt;
= Custom tax per product =&lt;br /&gt;
&lt;br /&gt;
This part will show you how to create a static custom tax per product. Static means that the tax will be stored per product, and not per product&#039;s line, thus you just have to setup the tax once per product and it will always be applied in every invoice you add this product.&lt;br /&gt;
&lt;br /&gt;
To do that, you will need the CustomFields Pro module + ProductsEasyAccess.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
1- Create a product&#039;s custom fields called &amp;quot;custom_tax&amp;quot;. Now, using ProductsEasyAccess, you can access this field wherever you wants, either to show it on the Dolibarr&#039;s interface (see previous chapter), or in a PDF template, which we will describe below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2- Modify your invoice&#039;s PDF template. You first need to load all the products&#039; fields &#039;&#039;before&#039;&#039; the loop over the products&#039; lines. To do this, search for a line like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// This should be placed above the &amp;quot;loop lines&amp;quot; below, add this:&lt;br /&gt;
&lt;br /&gt;
dol_include_once(&#039;/productseasyaccess/lib/productseasyaccess.lib.php&#039;);&lt;br /&gt;
fill_object_with_products_fields($object);&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines, search for this command&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  // Add this just below the for loop, this will ease your php manipulation after.&lt;br /&gt;
  // This will allow you to more easily access the products&#039; fields (this will store the id of each product inside $lineproductid).&lt;br /&gt;
  $line_product_id = $object-&amp;gt;lines[$i]-&amp;gt;fk_product;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4- You can now use your products&#039; fields in your PDF. To access the custom_tax field, just use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And to print it in your PDF:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell(0,3, $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax, 0, &#039;L&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5- Then, if you want to count the custom tax total and add it to your vat total, you can create a counter inside the for loop we cited in the first step:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_custom_tax = 0; // Initialize the custom tax total counter&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  $object-&amp;gt;total_custom_tax += $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then find inside the function **_tableau_tot**, find the following line:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And prepend the total modification like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc + $object-&amp;gt;total_custom_tax; // prepend this to add the custom tax to the total&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Done. This should show your custom tax per product and your total including all custom taxes.&lt;/div&gt;</summary>
		<author><name>Lrq3000</name></author>
	</entry>
	<entry>
		<id>https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33005</id>
		<title>Module CustomFields Cases</title>
		<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=33005"/>
		<updated>2014-12-22T13:01:32Z</updated>

		<summary type="html">&lt;p&gt;Lrq3000: /* Linking Dolibarr objects from two different modules */ fix mistake with php object creation&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:CustomFields]]&lt;br /&gt;
{{TemplateDocDevEn}}&lt;br /&gt;
{{TemplateModEN}}&lt;br /&gt;
{{BasculeDevUserEn|&lt;br /&gt;
name=CustomFields|&lt;br /&gt;
num=8500|&lt;br /&gt;
devdoc=[[Module_CustomFields_Dev]]|&lt;br /&gt;
userdoc=[[Module_CustomFields]]|}}&lt;br /&gt;
This page lists a compilation of concrete, practical examples uses of [[Module_CustomFields|CustomFields]] and of its various features.&lt;br /&gt;
&lt;br /&gt;
If you had an experience with CustomFields, you can also add here your case as an example for others to get inspired.&lt;br /&gt;
&lt;br /&gt;
= How to make a generic PDF template listing all custom fields =&lt;br /&gt;
&lt;br /&gt;
To make a generic PDF template listing all custom fields, you just need to take an existing PDF template, then you need to follow these 3 steps:&lt;br /&gt;
&lt;br /&gt;
1/ Edit the class name (required, else it will conflict with the original template).&lt;br /&gt;
&lt;br /&gt;
Eg: if we take the crabe template for invoices, we change:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;crabe&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
into:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_customfields extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;customfields&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2/ Copy this generic function at the end of the PDF:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propale/order/etc... (CustomFields simpler functions will automatically adapt)&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs); // set default PDF font size&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
    // Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
    $customfields = customfields_fill_object($object, null, $outputlangs, null, true);&lt;br /&gt;
&lt;br /&gt;
    // Setting the starting position of the text cursor&lt;br /&gt;
    $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
    $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
    // Printing the customfields&lt;br /&gt;
    foreach ($object-&amp;gt;customfields as $label=&amp;gt;$value) { // $value is already formatted!&lt;br /&gt;
        // Get translated label&lt;br /&gt;
        $translatedlabel = $customfields-&amp;gt;findLabelPDF($label, $outputlangs); // translated label of the customfield (not translated by default in customfields_fill_object() because a field should always be accessible by a base name, whatever the translation is)&lt;br /&gt;
&lt;br /&gt;
        // PDF formatting, placement and printing&lt;br /&gt;
        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
        $pdf-&amp;gt;MultiCell(0,3, $translatedlabel.&#039; ($object-&amp;gt;customfields-&amp;gt;&#039;.$label.&#039;): &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternative function (with a bit less functionalities, but more controllable):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page (used to debug if CustomFields setup is correct)&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propal/product/whatever...&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs);&lt;br /&gt;
&lt;br /&gt;
            if (empty($object-&amp;gt;table_element) or empty($object-&amp;gt;id)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;Current \$object is not compatible with CustomFields, could not find table_element or id.&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
    $customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element);&lt;br /&gt;
&lt;br /&gt;
            // Fetching custom fields records&lt;br /&gt;
            $fields = $customfields-&amp;gt;fetch($object-&amp;gt;id);&lt;br /&gt;
&lt;br /&gt;
            if (!isset($fields)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;No custom field could be found for this object. Please check your configuration (did you create at least one customfield and set a value in the current datasheet?)&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            } else {&lt;br /&gt;
                // Setting the starting position of the text cursor&lt;br /&gt;
                $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
                $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
                // Printing the customfields&lt;br /&gt;
                foreach ($fields as $key=&amp;gt;$field) {&lt;br /&gt;
                        $translatedname = $customfields-&amp;gt;findLabelPDF($key, $outputlangs); // label of the customfield&lt;br /&gt;
                        $value = $customfields-&amp;gt;simpleprintFieldPDF($key, $field, $outputlangs); // value (cleaned and properly formatted) of the customfield&lt;br /&gt;
&lt;br /&gt;
                        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
                        $pdf-&amp;gt;MultiCell(0,3, $translatedname.&#039;: &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
                        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3/ Call the _pagecustomfields function at the right place (generally, after _pagefoot and just before $pdf-&amp;gt;Close()):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Pied de page&lt;br /&gt;
$this-&amp;gt;_pagefoot($pdf,$object,$outputlangs);&lt;br /&gt;
$pdf-&amp;gt;AliasNbPages();&lt;br /&gt;
&lt;br /&gt;
// CustomFields&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) { // if the customfields module is activated...&lt;br /&gt;
        // Add a page with the customfields&lt;br /&gt;
        $pdf-&amp;gt;AddPage();&lt;br /&gt;
        $this-&amp;gt;_pagecustomfields($pdf,$object,$outputlangs);&lt;br /&gt;
        $pagenb++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
$pdf-&amp;gt;Close(); // you should first search for this, you should only find one occurrence&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then go to the module&#039;s admin panel (eg: invoice admin panel) and enable the template you&#039;ve just created.&lt;br /&gt;
&lt;br /&gt;
You can now use it to test if your customfields work.&lt;br /&gt;
&lt;br /&gt;
= Conditions on fields selection =&lt;br /&gt;
&lt;br /&gt;
Sometimes you may want to show a DropdownBox or Constraint (or another multi-choice type), but you may want to show or hide choices based on conditions (eg: only if it&#039;s a prospect, only if this zone was selected, only if this category was selected, etc..).&lt;br /&gt;
&lt;br /&gt;
To know which is the best solution you should choose to implement your condition, you should first know in which category your condition falls in.&lt;br /&gt;
&lt;br /&gt;
== Conditions categories ==&lt;br /&gt;
&lt;br /&gt;
Conditions can be categorized into 4 types:&lt;br /&gt;
&lt;br /&gt;
* static condition: choices are restrained on a condition that never changes (eg: only show third-parties that are suppliers, or lowercase all data on sql insertion, or check that a field is above a certain number, etc.).&lt;br /&gt;
* semi-dynamic condition: choices that are constrained on a static table but based on the value of another field/customfield. This is also commonly called &amp;quot;Cascaded dropdown lists&amp;quot; or &amp;quot;Dynamically linked dropdown lists&amp;quot;.&lt;br /&gt;
* dynamic: everything is dynamic: choices are based on a subfield of a field of the current module, so that there&#039;s no material table and it&#039;s impossible to create a view (because you would need to create one view per id) (eg: show only the contacts attached to the third-party being accessed)&lt;br /&gt;
* timed condition: a condition that is based on time or recurrence (eg: at 5:00 AM delete old records, etc..)&lt;br /&gt;
&lt;br /&gt;
== Conditions solutions ==&lt;br /&gt;
&lt;br /&gt;
Now that you know what kind of condition you want to set, you can choose one of the following solutions depending on your preferences and knowledge.&lt;br /&gt;
&lt;br /&gt;
In any case, you can always use the &#039;&#039;&#039;CustomFields&#039;s overload functions&#039;&#039;&#039; to fit your needs for nearly every type of condition.&lt;br /&gt;
&lt;br /&gt;
=== Static condition ===&lt;br /&gt;
&lt;br /&gt;
* View: create a view on the table based on the condition, and then create a custom field constrained on this view (which is just like any table), eg: only show third-parties that are suppliers:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
CREATE VIEW llx_societe_supplier_view AS&lt;br /&gt;
SELECT *&lt;br /&gt;
FROM llx_societe&lt;br /&gt;
WHERE fournisseur=1;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
WARNING: foreign keys (and thus constraints) can&#039;t be used on views with MySQL, and materialized views neither exist, so you can&#039;t use this method with MySQL.&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
* SQL Transformations&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
=== Semi-dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade option is ideal in most cases, and if not enough, you can use the Cascade Custom option and Overloading Functions.&lt;br /&gt;
* View&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
&lt;br /&gt;
=== Dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade Custom and Overload functions&lt;br /&gt;
* Making your own module, which calls the CustomFields class&lt;br /&gt;
&lt;br /&gt;
=== Timed condition ===&lt;br /&gt;
&lt;br /&gt;
* SQL scheduled events&lt;br /&gt;
* Cron job&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
= Overloading functions =&lt;br /&gt;
&lt;br /&gt;
Here are a few concrete use cases of overloading functions to achieve various goals.&lt;br /&gt;
&lt;br /&gt;
== Dynamic list selection based on another one ==&lt;br /&gt;
&lt;br /&gt;
The goal is to show two custom fields on the Third-Party module: one which gives the zone (secteur) of the third-party (Isere, Alpes du sud, Haute-Savoie...) and the other which gives relative to the zone the list of all the ski resorts (station_a) inside the selected zone.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will show how to manually manage this kind of requirements using [[Module_CustomFields#Overloading_functions:_Adding_your_own_management_code_for_custom_fields|overloading functions]], but in latest CustomFields releases, the same solution can be achieved automatically using the [[Module_CustomFields#Cascade|Cascade option]].&lt;br /&gt;
&lt;br /&gt;
We have one table &#039;&#039;&#039;llx_k_station&#039;&#039;&#039; (rowid, station (char50), secteur(char50)) which contains the following: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1,les 2 alpes,isère&lt;br /&gt;
2,chamrousse,isère&lt;br /&gt;
3,alpe d&#039;huez,isère&lt;br /&gt;
4,vars,alpes du sud&lt;br /&gt;
5,risoul,alpes du sud&lt;br /&gt;
etc...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In CustomFields&#039;s admin panel, we create two custom fields:&lt;br /&gt;
&lt;br /&gt;
1- secteur: returns the list of all zones - type DropdownBox: enum(&#039;- Aucun&#039;,&#039;Alpes du Sud&#039;,&#039;export&#039;,&#039;Haute-Savoie&#039;,&#039;Isère&#039;, etc...)&lt;br /&gt;
&lt;br /&gt;
2- station_a: returns the list of all ski resorts - type constraint on &#039;&#039;&#039;llx_k_station&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Here is the code to put in customfields_fields_extend.lib.php that will allow to show only the ski resorts corresponding to the selected zone:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_editview_societe_station_a (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$parameters, &amp;amp;$action, &amp;amp;$user, &amp;amp;$idvar, &amp;amp;$rightok, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
  global $db; // allow to access database functions&lt;br /&gt;
  $sql=&amp;quot;SELECT llx_k_station.station, llx_k_station.rowid FROM llx_societe_customfields INNER JOIN llx_k_station ON llx_societe_customfields.Secteur = llx_k_station.secteur WHERE llx_societe_customfields.fk_societe=&amp;quot;.$object-&amp;gt;id;&lt;br /&gt;
$result=$db-&amp;gt;query($sql);&lt;br /&gt;
 &lt;br /&gt;
  if ($result) {&lt;br /&gt;
    $num = $db-&amp;gt;num_rows($result);&lt;br /&gt;
    $i = 0;&lt;br /&gt;
    $myarray = array();&lt;br /&gt;
    while ($i &amp;lt; $num)&lt;br /&gt;
    {&lt;br /&gt;
      $obj = $db-&amp;gt;fetch_object($result);&lt;br /&gt;
      $myarray []=array(&#039;id&#039;=&amp;gt;$obj-&amp;gt;rowid, &#039;value&#039;=&amp;gt;$obj-&amp;gt;station);&lt;br /&gt;
      $i++;&lt;br /&gt;
    }&lt;br /&gt;
    $value = $myarray; &lt;br /&gt;
    $db-&amp;gt;free($result);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to manub for giving this case.&lt;br /&gt;
&lt;br /&gt;
== Dynamic discount computation per products lines on propales ==&lt;br /&gt;
&lt;br /&gt;
The goal here is to compute the discount for a product line in propales based on a computation on three custom fields.&lt;br /&gt;
&lt;br /&gt;
First, create three custom fields for &amp;quot;Line commercial proposals&amp;quot;: vl, cpl and lc.&lt;br /&gt;
&lt;br /&gt;
Then add the following overloading function at the end of your customfields_fields_extend.lib.php file (read the comments for more informations):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_propaldet_vl (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
&lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $vl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;vl&#039;};&lt;br /&gt;
    $cpl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;cpl&#039;};&lt;br /&gt;
    $lc = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;lc&#039;};&lt;br /&gt;
&lt;br /&gt;
    // Compute the discount&lt;br /&gt;
    $remise_calculee = (100 - $vl) * (100 - $cpl) * (100 - $lc);&lt;br /&gt;
&lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $remise_calculee = price2num($remise_calculee);&lt;br /&gt;
&lt;br /&gt;
    // Assign the new discount into the PropalLine object&lt;br /&gt;
    $object-&amp;gt;remise_percent = $remise_calculee;&lt;br /&gt;
&lt;br /&gt;
    // Update the price (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
&lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
&lt;br /&gt;
    //$q = &amp;quot;UPDATE  &amp;quot;.MAIN_DB_PREFIX.&amp;quot;propaldet SET remise_percent=&amp;quot;.$remise_calculee.&amp;quot; WHERE  rowid=&amp;quot;.$object-&amp;gt;rowid;&lt;br /&gt;
    //$db-&amp;gt;query($q);&lt;br /&gt;
    &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;vl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;cpl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;lc&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Linking Dolibarr objects from two different modules =&lt;br /&gt;
&lt;br /&gt;
Dolibarr sometimes offers the possibility to link two different modules. Eg: when you convert an Intervention card into an Invoice.&lt;br /&gt;
&lt;br /&gt;
In this case, you might want in your templates to fetch the custom fields of the linked object. Eg: you generate a PDF from the Invoice, but you want to get the custom fields from the Intervention that was converted into the Invoice.&lt;br /&gt;
&lt;br /&gt;
The key here is to use the &#039;&#039;&#039;llx_element_element&#039;&#039;&#039; table or the &#039;&#039;&#039;llx_element_*&#039;&#039;&#039; tables (eg: llx_element_contact): this is a standard Dolibarr table that stores all links between objects of two different modules.&lt;br /&gt;
&lt;br /&gt;
Here is the structure:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
rowid | fk_source | sourcetype | fk_target | targettype&lt;br /&gt;
-------------------------------------------------------&lt;br /&gt;
1 | 2 | fichinter | 5 | facture&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can then use a php code like the following to populate your $object with customfields:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
// This will also create and returns us an instance of the CustomFields class, which we will use to manually query the llx_element_element table&lt;br /&gt;
$customfields = customfields_fill_object($object, null, $outputlangs, &#039;facture&#039;, true); // beautified values&lt;br /&gt;
&lt;br /&gt;
// Querying the llx_element_element table manually to get the link between the invoice and the linked intervention card (we want the id of the intervention card)&lt;br /&gt;
// fetchAny($columns, $table, $where=&#039;&#039;, $orderby=&#039;&#039;, $limitby=&#039;&#039;)&lt;br /&gt;
$eltrow = $customfields-&amp;gt;fetchAny(&#039;*&#039;, MAIN_DB_PREFIX.&#039;element_element&#039;, &#039;targettype=&amp;quot;&#039;.$object-&amp;gt;element.&#039;&amp;quot; and fk_target=&#039;.$object-&amp;gt;id.&#039; and sourcetype=&amp;quot;fichinter&amp;quot;&#039;);&lt;br /&gt;
&lt;br /&gt;
// Save the intervention&#039;s id&lt;br /&gt;
$sourceid = $eltrow[0][&#039;fk_source&#039;];&lt;br /&gt;
&lt;br /&gt;
// Crafting a dummy intervention object&lt;br /&gt;
$fromobject = new stdClass(); // Init an empty object&lt;br /&gt;
$fromobject-&amp;gt;id = $sourceid; // We need the id and..&lt;br /&gt;
$fromobject-&amp;gt;table_element = &#039;fichinter&#039;; // the table_element (name of the table)&lt;br /&gt;
&lt;br /&gt;
// We can then fill $object with the intervention custom fields (we store them in a sub property &#039;fichinter&#039; to avoid conflicts with invoice&#039;s custom fields)&lt;br /&gt;
customfields_fill_object($object, $fromobject, $outputlangs, &#039;fichinter&#039;, true);&lt;br /&gt;
&lt;br /&gt;
// Now you can access invoices custom fields using:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;facture-&amp;gt;cf_myfield);&lt;br /&gt;
// And access intervention custom fields with:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;fichinter-&amp;gt;cf_myfield);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to netassopro for the tip.&lt;br /&gt;
&lt;br /&gt;
= Modify the total price of every product/service line with a coefficient (without tampering the discount) =&lt;br /&gt;
&lt;br /&gt;
Here we will go through the creation and management of a custom field called &#039;&#039;&#039;coefficient&#039;&#039;&#039; that will change the total price of each product by modifying it by the coefficient.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will not create a coefficient per product but rather per product&#039;s line. Thus if you want to create a coefficient that never change between invoices but only depending on the product and stays static, please refer to the next chapter below about creating a custom tax per product.&lt;br /&gt;
&lt;br /&gt;
The preliminary and necessary step is to first create a custom field that will be used to modify the total price. We will create one for Client Invoice Lines.&lt;br /&gt;
&lt;br /&gt;
Go to the CustomFields administration panel, then into the Client Invoice Lines tab, then click on the button New Field, name the field &#039;&#039;&#039;coefficient&#039;&#039;&#039;, and set the type to &#039;&#039;&#039;Double&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
When you go to client invoices in Dolibarr, you should then see something similar to this:&lt;br /&gt;
[[File:Cfe1.jpg]]&lt;br /&gt;
You can see that our coefficient field was successfully created.&lt;br /&gt;
&lt;br /&gt;
Then here are two alternative ways of implementing the change of the total price of each product relativery to this coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change total price in database ==&lt;br /&gt;
&lt;br /&gt;
Here we will use a &amp;quot;save&amp;quot; overloading function to change the total price directly in the database, thus we won&#039;t have to modify anything else (eg: nor PDF templating nor ODT) since the total price will be already updated in the database.&lt;br /&gt;
&lt;br /&gt;
Warning: this method will work only with CustomFields =&amp;gt; 3.2.23 since the &amp;quot;save&amp;quot; overloading function did not work for products lines before.&lt;br /&gt;
&lt;br /&gt;
Just add the following overloading function in your customfields_fields_extend.lib.php file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_facturedet_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
 &lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $coefficient = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;coefficient&#039;}; // you could also use $coefficient = $value;&lt;br /&gt;
 &lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $coefficient = price2num($coefficient);&lt;br /&gt;
 &lt;br /&gt;
    // Get the original subprices (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
 &lt;br /&gt;
    // Update the subprices with the coefficient&lt;br /&gt;
    $object-&amp;gt;total_ht = $object-&amp;gt;total_ht * $coefficient; // compute no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva = $object-&amp;gt;total_tva * $coefficient; // compute tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc * $coefficient; // compute tax included price&lt;br /&gt;
 &lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(&#039;&#039;,true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
 &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;coefficient&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should show the correct total price on the Dolibarr&#039;s interface without modifying the unit subprice (P.U. Unit):&lt;br /&gt;
&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
You have nothing to edit in your PDF or ODT template, since the new subprice with applied coefficient will be automatically printed instead of the old one without coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change printed total price per product without tampering the subprice in the database ==&lt;br /&gt;
&lt;br /&gt;
Here is a way to change the printed total price (the one shown on screen and in PDF/ODT) but without changing the total price in the database.&lt;br /&gt;
&lt;br /&gt;
The following sub-chapters are all independent, thus you can only follow the ones that you require, eg: if you only need to change the price in your PDF documents, just create a custom field then jump to the sub-chapter to &#039;&#039;&#039;Implement the change of price in PDF&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in ODT templates ===&lt;br /&gt;
&lt;br /&gt;
There&#039;s no eval implementation currently inside ODT templates, but there is a kind of hook implementation, called [[Create_an_ODT_document_template#Other_personalized_tags|substitution functions]].&lt;br /&gt;
&lt;br /&gt;
You can either create your own module, then create your own substitution function, but for the sake of simplicity in this tutorial, we will directly use the CustomFields substitution function. However, please remember that you will have to redo your changes everytime you will update the CustomFields module, so if you don&#039;t want to do that it would be better if you create your own module and substitution function.&lt;br /&gt;
&lt;br /&gt;
Open the file &#039;&#039;&#039;htdocs/customfields/core/substitutions/functions_customfields.lib.php&#039;&#039;&#039; and go to the function &#039;&#039;&#039;customfields_completesubstitutionarray_lines&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
At the very end of this function, but inside the customfields check:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) {&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
But just before the ending bracket, you should add the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($object-&amp;gt;table_element_line === &#039;facturedet&#039;) { // First check that we are in the right module, we don&#039;t want to use coefficient for other modules if we didn&#039;t define the custom field there&lt;br /&gt;
    $substitutionarray[&#039;line_price_full&#039;] = $substitutionarray[&#039;line_price_ttc&#039;] * $substitutionarray[&#039;cf_coefficient&#039;]; // multiply the total price with our coefficient&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code will add a new tag &#039;&#039;&#039;{line_price_full}&#039;&#039;&#039; that you can then use inside your ODT templates.&lt;br /&gt;
&lt;br /&gt;
Just place this tag in a table in your ODT document, between the table tags ( [!-- BEGIN row.lines --] ... [!-- END row.lines --] ), and this will print your full price.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in PDF templates ===&lt;br /&gt;
&lt;br /&gt;
First, we will create our own PDF template by copying an existing template, and then we will edit it to change the price of items.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Creating your own PDF template by copying an existing template&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Go into the following folder: &#039;&#039;&#039;htdocs/core/modules/facture/doc/&#039;&#039;&#039;, then copy the file &#039;&#039;&#039;pdf_crabe.modules.php&#039;&#039;&#039; and name the new one &#039;&#039;&#039;pdf_crabecoeff.modules.php&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Then open it in your favourite editor, and change the following lines:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures // change pdf_crabe into pdf_crabecoeff&lt;br /&gt;
{&lt;br /&gt;
...&lt;br /&gt;
    function __construct($db)&lt;br /&gt;
    {&lt;br /&gt;
        ...&lt;br /&gt;
	$this-&amp;gt;name = &amp;quot;crabe&amp;quot;; // change &amp;quot;crabe&amp;quot; into &amp;quot;crabecoeff&amp;quot;&lt;br /&gt;
        ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you have your own PDF template!&lt;br /&gt;
&lt;br /&gt;
Now you should &#039;&#039;&#039;enable your PDF template&#039;&#039;&#039; before doing anything else, by going into the Modules Administration panel, and into the Invoice admin page, then click on the ON button at the right of the &#039;&#039;&#039;crabecoeff&#039;&#039;&#039; entry to enable the template.&lt;br /&gt;
&lt;br /&gt;
Now, let&#039;s proceed onto modifying the prices.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Changing the prices automatically in your PDF&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
You can now do pretty much anything you want in your PDF template, but I will give you a mean to automatically change the prices of every products with our coefficient, as well as the total price, without editing too much the template.&lt;br /&gt;
&lt;br /&gt;
First, try to find the following code of line (should be inside the write_file() method):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf=pdf_getInstance($this-&amp;gt;format);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, just copy/paste the following code &#039;&#039;&#039;below&#039;&#039;&#039; pdf_getInstance():&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$this-&amp;gt;customfields = customfields_fill_object($object, null, $outputlangs, null, true); // beautified values&lt;br /&gt;
$this-&amp;gt;customfields_raw = customfields_fill_object($object, null, $outputlangs, &#039;raw&#039;, null); // raw values&lt;br /&gt;
$this-&amp;gt;customfields_lines = customfields_fill_object_lines($object, null, $outputlangs, null, true); // product lines&#039; values&lt;br /&gt;
&lt;br /&gt;
// Resetting every $object price because we will compute them anew&lt;br /&gt;
$object-&amp;gt;total_ht = 0;&lt;br /&gt;
$object-&amp;gt;total_tva = 0;&lt;br /&gt;
$object-&amp;gt;total_ttc = 0;&lt;br /&gt;
// For each line of product/service, we will recompute all the prices&lt;br /&gt;
foreach($object-&amp;gt;lines as $line) {&lt;br /&gt;
    // Get the coefficient custom field&#039;s value for the current line&lt;br /&gt;
    $coeff = $object-&amp;gt;customfields-&amp;gt;lines-&amp;gt;{$line-&amp;gt;rowid}-&amp;gt;cf_coefficient;&lt;br /&gt;
    // Recompute the price of this product/service line&lt;br /&gt;
    $line-&amp;gt;total_ht = $line-&amp;gt;total_ht*$coeff; // compute no tax price&lt;br /&gt;
    $line-&amp;gt;total_tva = $line-&amp;gt;total_tva*$coeff; // compute tax amount&lt;br /&gt;
    $line-&amp;gt;total_ttc = $line-&amp;gt;total_ttc*$coeff; // compute tax included price&lt;br /&gt;
    // Do the sum of all new products/services prices and tva (will be incremented with every product/service line)&lt;br /&gt;
    $object-&amp;gt;total_ht += $line-&amp;gt;total_ht; // global no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva += $line-&amp;gt;total_tva; // global vat/tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc += $line-&amp;gt;total_ttc; // global  tax included price&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you should try to generate your PDF document, and you should get something like this:&lt;br /&gt;
[[File:Cfe3.jpg]]&lt;br /&gt;
&lt;br /&gt;
Note: printing the coefficient as a column in your PDF document is also possible but will not be covered here because PDF templates will vary for every persons depending on your needs.&lt;br /&gt;
For guidelines about how to do this, you can read the documentation on [[Create_a_PDF_document_template|how to create a PDF template]] and [[Module_CustomFields#Implementing_in_PDF_templates|how to use custom fields in PDF templates]].&lt;br /&gt;
&lt;br /&gt;
Hint: you should be looking inside the following conditional block, inside the write_file() method:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
{&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
Unluckily there is not (yet) any hooking context to modify how lines of products/services are printed, thus the only thing you can do is directly edit the &#039;&#039;&#039;template file&#039;&#039;&#039; and manually put inside your code to load your custom field and print it the way you want using HTML formatting, for our example:&lt;br /&gt;
&lt;br /&gt;
* For Dolibarr 3.3 and above, edit the file &#039;&#039;&#039;htdocs/core/tpl/objectline_view.tpl.php&#039;&#039;&#039;&lt;br /&gt;
* For Dolibarr 3.1 and 3.2, edit the file &#039;&#039;&#039;htdocs/core/tpl/freeproductline_view.tpl.php&#039;&#039;&#039; AND &#039;&#039;&#039;predefinedproductline_view.tpl.php&#039;&#039;&#039; (apply the same changes to both files)&lt;br /&gt;
&lt;br /&gt;
In the template file you opened, find the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And replace it by:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = $line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = &#039;facturedet&#039;; // you can use $this-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
&lt;br /&gt;
// Modifying the HT price and printing it on the Dolibarr&#039;s datasheet interface&lt;br /&gt;
// Applying a multiplying/dividing coefficient to an HT price will give a mathematically correct TTC price since the taxes are also a multiplier&lt;br /&gt;
// However, if you want to apply other mathematical operations (like addition or substaction), you should use the total price with $line-&amp;gt;total_ttc&lt;br /&gt;
echo price($line-&amp;gt;total_ht * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient);&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should produce something like this:&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Technical note: here we are using customfields_fill_object() instead of customfields_fill_object_lines() (the latter being specifically adapted to lines and thus should theoretically be used here) for performance reasons because the template file we are editing is called in a loop, and customfields_fill_object_lines() should be called outside a loop (because it already does the job that is done by the loop), so here we &#039;tricks&#039; the customfields_fill_object() function to avoid this loop by submitting &#039;facturedet&#039; as a module by itself (when in fact it&#039;s a product lines table, but it works the same way nevertheless). This is a good demonstration of the flexibility of the CustomFields functions and methods.&lt;br /&gt;
&lt;br /&gt;
= Custom tax per product =&lt;br /&gt;
&lt;br /&gt;
This part will show you how to create a static custom tax per product. Static means that the tax will be stored per product, and not per product&#039;s line, thus you just have to setup the tax once per product and it will always be applied in every invoice you add this product.&lt;br /&gt;
&lt;br /&gt;
To do that, you will need the CustomFields Pro module + ProductsEasyAccess.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
1- Create a product&#039;s custom fields called &amp;quot;custom_tax&amp;quot;. Now, using ProductsEasyAccess, you can access this field wherever you wants, either to show it on the Dolibarr&#039;s interface (see previous chapter), or in a PDF template, which we will describe below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2- Modify your invoice&#039;s PDF template. You first need to load all the products&#039; fields &#039;&#039;before&#039;&#039; the loop over the products&#039; lines. To do this, search for a line like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// This should be placed above the &amp;quot;loop lines&amp;quot; below, add this:&lt;br /&gt;
&lt;br /&gt;
dol_include_once(&#039;/productseasyaccess/lib/productseasyaccess.lib.php&#039;);&lt;br /&gt;
fill_object_with_products_fields($object);&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines, search for this command&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  // Add this just below the for loop, this will ease your php manipulation after.&lt;br /&gt;
  // This will allow you to more easily access the products&#039; fields (this will store the id of each product inside $lineproductid).&lt;br /&gt;
  $line_product_id = $object-&amp;gt;lines[$i]-&amp;gt;fk_product;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4- You can now use your products&#039; fields in your PDF. To access the custom_tax field, just use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And to print it in your PDF:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell(0,3, $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax, 0, &#039;L&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5- Then, if you want to count the custom tax total and add it to your vat total, you can create a counter inside the for loop we cited in the first step:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_custom_tax = 0; // Initialize the custom tax total counter&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  $object-&amp;gt;total_custom_tax += $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then find inside the function **_tableau_tot**, find the following line:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And prepend the total modification like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc + $object-&amp;gt;total_custom_tax; // prepend this to add the custom tax to the total&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Done. This should show your custom tax per product and your total including all custom taxes.&lt;/div&gt;</summary>
		<author><name>Lrq3000</name></author>
	</entry>
	<entry>
		<id>https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=32995</id>
		<title>Module CustomFields Cases</title>
		<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=32995"/>
		<updated>2014-12-17T14:30:43Z</updated>

		<summary type="html">&lt;p&gt;Lrq3000: /* Change total price in database */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:CustomFields]]&lt;br /&gt;
{{TemplateDocDevEn}}&lt;br /&gt;
{{TemplateModEN}}&lt;br /&gt;
{{BasculeDevUserEn|&lt;br /&gt;
name=CustomFields|&lt;br /&gt;
num=8500|&lt;br /&gt;
devdoc=[[Module_CustomFields_Dev]]|&lt;br /&gt;
userdoc=[[Module_CustomFields]]|}}&lt;br /&gt;
This page lists a compilation of concrete, practical examples uses of [[Module_CustomFields|CustomFields]] and of its various features.&lt;br /&gt;
&lt;br /&gt;
If you had an experience with CustomFields, you can also add here your case as an example for others to get inspired.&lt;br /&gt;
&lt;br /&gt;
= How to make a generic PDF template listing all custom fields =&lt;br /&gt;
&lt;br /&gt;
To make a generic PDF template listing all custom fields, you just need to take an existing PDF template, then you need to follow these 3 steps:&lt;br /&gt;
&lt;br /&gt;
1/ Edit the class name (required, else it will conflict with the original template).&lt;br /&gt;
&lt;br /&gt;
Eg: if we take the crabe template for invoices, we change:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;crabe&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
into:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_customfields extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;customfields&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2/ Copy this generic function at the end of the PDF:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propale/order/etc... (CustomFields simpler functions will automatically adapt)&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs); // set default PDF font size&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
    // Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
    $customfields = customfields_fill_object($object, null, $outputlangs, null, true);&lt;br /&gt;
&lt;br /&gt;
    // Setting the starting position of the text cursor&lt;br /&gt;
    $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
    $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
    // Printing the customfields&lt;br /&gt;
    foreach ($object-&amp;gt;customfields as $label=&amp;gt;$value) { // $value is already formatted!&lt;br /&gt;
        // Get translated label&lt;br /&gt;
        $translatedlabel = $customfields-&amp;gt;findLabelPDF($label, $outputlangs); // translated label of the customfield (not translated by default in customfields_fill_object() because a field should always be accessible by a base name, whatever the translation is)&lt;br /&gt;
&lt;br /&gt;
        // PDF formatting, placement and printing&lt;br /&gt;
        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
        $pdf-&amp;gt;MultiCell(0,3, $translatedlabel.&#039; ($object-&amp;gt;customfields-&amp;gt;&#039;.$label.&#039;): &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternative function (with a bit less functionalities, but more controllable):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page (used to debug if CustomFields setup is correct)&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propal/product/whatever...&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs);&lt;br /&gt;
&lt;br /&gt;
            if (empty($object-&amp;gt;table_element) or empty($object-&amp;gt;id)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;Current \$object is not compatible with CustomFields, could not find table_element or id.&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
    $customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element);&lt;br /&gt;
&lt;br /&gt;
            // Fetching custom fields records&lt;br /&gt;
            $fields = $customfields-&amp;gt;fetch($object-&amp;gt;id);&lt;br /&gt;
&lt;br /&gt;
            if (!isset($fields)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;No custom field could be found for this object. Please check your configuration (did you create at least one customfield and set a value in the current datasheet?)&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            } else {&lt;br /&gt;
                // Setting the starting position of the text cursor&lt;br /&gt;
                $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
                $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
                // Printing the customfields&lt;br /&gt;
                foreach ($fields as $key=&amp;gt;$field) {&lt;br /&gt;
                        $translatedname = $customfields-&amp;gt;findLabelPDF($key, $outputlangs); // label of the customfield&lt;br /&gt;
                        $value = $customfields-&amp;gt;simpleprintFieldPDF($key, $field, $outputlangs); // value (cleaned and properly formatted) of the customfield&lt;br /&gt;
&lt;br /&gt;
                        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
                        $pdf-&amp;gt;MultiCell(0,3, $translatedname.&#039;: &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
                        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3/ Call the _pagecustomfields function at the right place (generally, after _pagefoot and just before $pdf-&amp;gt;Close()):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Pied de page&lt;br /&gt;
$this-&amp;gt;_pagefoot($pdf,$object,$outputlangs);&lt;br /&gt;
$pdf-&amp;gt;AliasNbPages();&lt;br /&gt;
&lt;br /&gt;
// CustomFields&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) { // if the customfields module is activated...&lt;br /&gt;
        // Add a page with the customfields&lt;br /&gt;
        $pdf-&amp;gt;AddPage();&lt;br /&gt;
        $this-&amp;gt;_pagecustomfields($pdf,$object,$outputlangs);&lt;br /&gt;
        $pagenb++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
$pdf-&amp;gt;Close(); // you should first search for this, you should only find one occurrence&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then go to the module&#039;s admin panel (eg: invoice admin panel) and enable the template you&#039;ve just created.&lt;br /&gt;
&lt;br /&gt;
You can now use it to test if your customfields work.&lt;br /&gt;
&lt;br /&gt;
= Conditions on fields selection =&lt;br /&gt;
&lt;br /&gt;
Sometimes you may want to show a DropdownBox or Constraint (or another multi-choice type), but you may want to show or hide choices based on conditions (eg: only if it&#039;s a prospect, only if this zone was selected, only if this category was selected, etc..).&lt;br /&gt;
&lt;br /&gt;
To know which is the best solution you should choose to implement your condition, you should first know in which category your condition falls in.&lt;br /&gt;
&lt;br /&gt;
== Conditions categories ==&lt;br /&gt;
&lt;br /&gt;
Conditions can be categorized into 4 types:&lt;br /&gt;
&lt;br /&gt;
* static condition: choices are restrained on a condition that never changes (eg: only show third-parties that are suppliers, or lowercase all data on sql insertion, or check that a field is above a certain number, etc.).&lt;br /&gt;
* semi-dynamic condition: choices that are constrained on a static table but based on the value of another field/customfield. This is also commonly called &amp;quot;Cascaded dropdown lists&amp;quot; or &amp;quot;Dynamically linked dropdown lists&amp;quot;.&lt;br /&gt;
* dynamic: everything is dynamic: choices are based on a subfield of a field of the current module, so that there&#039;s no material table and it&#039;s impossible to create a view (because you would need to create one view per id) (eg: show only the contacts attached to the third-party being accessed)&lt;br /&gt;
* timed condition: a condition that is based on time or recurrence (eg: at 5:00 AM delete old records, etc..)&lt;br /&gt;
&lt;br /&gt;
== Conditions solutions ==&lt;br /&gt;
&lt;br /&gt;
Now that you know what kind of condition you want to set, you can choose one of the following solutions depending on your preferences and knowledge.&lt;br /&gt;
&lt;br /&gt;
In any case, you can always use the &#039;&#039;&#039;CustomFields&#039;s overload functions&#039;&#039;&#039; to fit your needs for nearly every type of condition.&lt;br /&gt;
&lt;br /&gt;
=== Static condition ===&lt;br /&gt;
&lt;br /&gt;
* View: create a view on the table based on the condition, and then create a custom field constrained on this view (which is just like any table), eg: only show third-parties that are suppliers:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
CREATE VIEW llx_societe_supplier_view AS&lt;br /&gt;
SELECT *&lt;br /&gt;
FROM llx_societe&lt;br /&gt;
WHERE fournisseur=1;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
WARNING: foreign keys (and thus constraints) can&#039;t be used on views with MySQL, and materialized views neither exist, so you can&#039;t use this method with MySQL.&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
* SQL Transformations&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
=== Semi-dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade option is ideal in most cases, and if not enough, you can use the Cascade Custom option and Overloading Functions.&lt;br /&gt;
* View&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
&lt;br /&gt;
=== Dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade Custom and Overload functions&lt;br /&gt;
* Making your own module, which calls the CustomFields class&lt;br /&gt;
&lt;br /&gt;
=== Timed condition ===&lt;br /&gt;
&lt;br /&gt;
* SQL scheduled events&lt;br /&gt;
* Cron job&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
= Overloading functions =&lt;br /&gt;
&lt;br /&gt;
Here are a few concrete use cases of overloading functions to achieve various goals.&lt;br /&gt;
&lt;br /&gt;
== Dynamic list selection based on another one ==&lt;br /&gt;
&lt;br /&gt;
The goal is to show two custom fields on the Third-Party module: one which gives the zone (secteur) of the third-party (Isere, Alpes du sud, Haute-Savoie...) and the other which gives relative to the zone the list of all the ski resorts (station_a) inside the selected zone.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will show how to manually manage this kind of requirements using [[Module_CustomFields#Overloading_functions:_Adding_your_own_management_code_for_custom_fields|overloading functions]], but in latest CustomFields releases, the same solution can be achieved automatically using the [[Module_CustomFields#Cascade|Cascade option]].&lt;br /&gt;
&lt;br /&gt;
We have one table &#039;&#039;&#039;llx_k_station&#039;&#039;&#039; (rowid, station (char50), secteur(char50)) which contains the following: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1,les 2 alpes,isère&lt;br /&gt;
2,chamrousse,isère&lt;br /&gt;
3,alpe d&#039;huez,isère&lt;br /&gt;
4,vars,alpes du sud&lt;br /&gt;
5,risoul,alpes du sud&lt;br /&gt;
etc...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In CustomFields&#039;s admin panel, we create two custom fields:&lt;br /&gt;
&lt;br /&gt;
1- secteur: returns the list of all zones - type DropdownBox: enum(&#039;- Aucun&#039;,&#039;Alpes du Sud&#039;,&#039;export&#039;,&#039;Haute-Savoie&#039;,&#039;Isère&#039;, etc...)&lt;br /&gt;
&lt;br /&gt;
2- station_a: returns the list of all ski resorts - type constraint on &#039;&#039;&#039;llx_k_station&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Here is the code to put in customfields_fields_extend.lib.php that will allow to show only the ski resorts corresponding to the selected zone:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_editview_societe_station_a (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$parameters, &amp;amp;$action, &amp;amp;$user, &amp;amp;$idvar, &amp;amp;$rightok, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
  global $db; // allow to access database functions&lt;br /&gt;
  $sql=&amp;quot;SELECT llx_k_station.station, llx_k_station.rowid FROM llx_societe_customfields INNER JOIN llx_k_station ON llx_societe_customfields.Secteur = llx_k_station.secteur WHERE llx_societe_customfields.fk_societe=&amp;quot;.$object-&amp;gt;id;&lt;br /&gt;
$result=$db-&amp;gt;query($sql);&lt;br /&gt;
 &lt;br /&gt;
  if ($result) {&lt;br /&gt;
    $num = $db-&amp;gt;num_rows($result);&lt;br /&gt;
    $i = 0;&lt;br /&gt;
    $myarray = array();&lt;br /&gt;
    while ($i &amp;lt; $num)&lt;br /&gt;
    {&lt;br /&gt;
      $obj = $db-&amp;gt;fetch_object($result);&lt;br /&gt;
      $myarray []=array(&#039;id&#039;=&amp;gt;$obj-&amp;gt;rowid, &#039;value&#039;=&amp;gt;$obj-&amp;gt;station);&lt;br /&gt;
      $i++;&lt;br /&gt;
    }&lt;br /&gt;
    $value = $myarray; &lt;br /&gt;
    $db-&amp;gt;free($result);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to manub for giving this case.&lt;br /&gt;
&lt;br /&gt;
== Dynamic discount computation per products lines on propales ==&lt;br /&gt;
&lt;br /&gt;
The goal here is to compute the discount for a product line in propales based on a computation on three custom fields.&lt;br /&gt;
&lt;br /&gt;
First, create three custom fields for &amp;quot;Line commercial proposals&amp;quot;: vl, cpl and lc.&lt;br /&gt;
&lt;br /&gt;
Then add the following overloading function at the end of your customfields_fields_extend.lib.php file (read the comments for more informations):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_propaldet_vl (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
&lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $vl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;vl&#039;};&lt;br /&gt;
    $cpl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;cpl&#039;};&lt;br /&gt;
    $lc = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;lc&#039;};&lt;br /&gt;
&lt;br /&gt;
    // Compute the discount&lt;br /&gt;
    $remise_calculee = (100 - $vl) * (100 - $cpl) * (100 - $lc);&lt;br /&gt;
&lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $remise_calculee = price2num($remise_calculee);&lt;br /&gt;
&lt;br /&gt;
    // Assign the new discount into the PropalLine object&lt;br /&gt;
    $object-&amp;gt;remise_percent = $remise_calculee;&lt;br /&gt;
&lt;br /&gt;
    // Update the price (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
&lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
&lt;br /&gt;
    //$q = &amp;quot;UPDATE  &amp;quot;.MAIN_DB_PREFIX.&amp;quot;propaldet SET remise_percent=&amp;quot;.$remise_calculee.&amp;quot; WHERE  rowid=&amp;quot;.$object-&amp;gt;rowid;&lt;br /&gt;
    //$db-&amp;gt;query($q);&lt;br /&gt;
    &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;vl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;cpl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;lc&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Linking Dolibarr objects from two different modules =&lt;br /&gt;
&lt;br /&gt;
Dolibarr sometimes offers the possibility to link two different modules. Eg: when you convert an Intervention card into an Invoice.&lt;br /&gt;
&lt;br /&gt;
In this case, you might want in your templates to fetch the custom fields of the linked object. Eg: you generate a PDF from the Invoice, but you want to get the custom fields from the Intervention that was converted into the Invoice.&lt;br /&gt;
&lt;br /&gt;
The key here is to use the &#039;&#039;&#039;llx_element_element&#039;&#039;&#039; table or the &#039;&#039;&#039;llx_element_*&#039;&#039;&#039; tables (eg: llx_element_contact): this is a standard Dolibarr table that stores all links between objects of two different modules.&lt;br /&gt;
&lt;br /&gt;
Here is the structure:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
rowid | fk_source | sourcetype | fk_target | targettype&lt;br /&gt;
-------------------------------------------------------&lt;br /&gt;
1 | 2 | fichinter | 5 | facture&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can then use a php code like the following to populate your $object with customfields:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
// This will also create and returns us an instance of the CustomFields class, which we will use to manually query the llx_element_element table&lt;br /&gt;
$customfields = customfields_fill_object($object, null, $outputlangs, &#039;facture&#039;, true); // beautified values&lt;br /&gt;
&lt;br /&gt;
// Querying the llx_element_element table manually to get the link between the invoice and the linked intervention card (we want the id of the intervention card)&lt;br /&gt;
// fetchAny($columns, $table, $where=&#039;&#039;, $orderby=&#039;&#039;, $limitby=&#039;&#039;)&lt;br /&gt;
$eltrow = $customfields-&amp;gt;fetchAny(&#039;*&#039;, MAIN_DB_PREFIX.&#039;element_element&#039;, &#039;targettype=&amp;quot;&#039;.$object-&amp;gt;element.&#039;&amp;quot; and fk_target=&#039;.$object-&amp;gt;id.&#039; and sourcetype=&amp;quot;fichinter&amp;quot;&#039;);&lt;br /&gt;
&lt;br /&gt;
// Save the intervention&#039;s id&lt;br /&gt;
$sourceid = $eltrow[0][&#039;fk_source&#039;];&lt;br /&gt;
&lt;br /&gt;
// Crafting a dummy intervention object&lt;br /&gt;
$fromobject = object(); // Init the object&lt;br /&gt;
$fromobject-&amp;gt;id = $sourceid; // We need the id and..&lt;br /&gt;
$fromobject-&amp;gt;table_element = &#039;fichinter&#039;; // the table_element (name of the table)&lt;br /&gt;
&lt;br /&gt;
// We can then fill $object with the intervention custom fields (we store them in a sub property &#039;fichinter&#039; to avoid conflicts with invoice&#039;s custom fields)&lt;br /&gt;
customfields_fill_object($object, $fromobject, $outputlangs, &#039;fichinter&#039;, true);&lt;br /&gt;
&lt;br /&gt;
// Now you can access invoices custom fields using:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;facture-&amp;gt;cf_myfield);&lt;br /&gt;
// And access intervention custom fields with:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;fichinter-&amp;gt;cf_myfield);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to netassopro for the tip.&lt;br /&gt;
&lt;br /&gt;
= Modify the total price of every product/service line with a coefficient (without tampering the discount) =&lt;br /&gt;
&lt;br /&gt;
Here we will go through the creation and management of a custom field called &#039;&#039;&#039;coefficient&#039;&#039;&#039; that will change the total price of each product by modifying it by the coefficient.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will not create a coefficient per product but rather per product&#039;s line. Thus if you want to create a coefficient that never change between invoices but only depending on the product and stays static, please refer to the next chapter below about creating a custom tax per product.&lt;br /&gt;
&lt;br /&gt;
The preliminary and necessary step is to first create a custom field that will be used to modify the total price. We will create one for Client Invoice Lines.&lt;br /&gt;
&lt;br /&gt;
Go to the CustomFields administration panel, then into the Client Invoice Lines tab, then click on the button New Field, name the field &#039;&#039;&#039;coefficient&#039;&#039;&#039;, and set the type to &#039;&#039;&#039;Double&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
When you go to client invoices in Dolibarr, you should then see something similar to this:&lt;br /&gt;
[[File:Cfe1.jpg]]&lt;br /&gt;
You can see that our coefficient field was successfully created.&lt;br /&gt;
&lt;br /&gt;
Then here are two alternative ways of implementing the change of the total price of each product relativery to this coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change total price in database ==&lt;br /&gt;
&lt;br /&gt;
Here we will use a &amp;quot;save&amp;quot; overloading function to change the total price directly in the database, thus we won&#039;t have to modify anything else (eg: nor PDF templating nor ODT) since the total price will be already updated in the database.&lt;br /&gt;
&lt;br /&gt;
Warning: this method will work only with CustomFields =&amp;gt; 3.2.23 since the &amp;quot;save&amp;quot; overloading function did not work for products lines before.&lt;br /&gt;
&lt;br /&gt;
Just add the following overloading function in your customfields_fields_extend.lib.php file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_facturedet_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
 &lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $coefficient = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;coefficient&#039;}; // you could also use $coefficient = $value;&lt;br /&gt;
 &lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $coefficient = price2num($coefficient);&lt;br /&gt;
 &lt;br /&gt;
    // Get the original subprices (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
 &lt;br /&gt;
    // Update the subprices with the coefficient&lt;br /&gt;
    $object-&amp;gt;total_ht = $object-&amp;gt;total_ht * $coefficient; // compute no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva = $object-&amp;gt;total_tva * $coefficient; // compute tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc * $coefficient; // compute tax included price&lt;br /&gt;
 &lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(&#039;&#039;,true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
 &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;coefficient&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should show the correct total price on the Dolibarr&#039;s interface without modifying the unit subprice (P.U. Unit):&lt;br /&gt;
&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
You have nothing to edit in your PDF or ODT template, since the new subprice with applied coefficient will be automatically printed instead of the old one without coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change printed total price per product without tampering the subprice in the database ==&lt;br /&gt;
&lt;br /&gt;
Here is a way to change the printed total price (the one shown on screen and in PDF/ODT) but without changing the total price in the database.&lt;br /&gt;
&lt;br /&gt;
The following sub-chapters are all independent, thus you can only follow the ones that you require, eg: if you only need to change the price in your PDF documents, just create a custom field then jump to the sub-chapter to &#039;&#039;&#039;Implement the change of price in PDF&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in ODT templates ===&lt;br /&gt;
&lt;br /&gt;
There&#039;s no eval implementation currently inside ODT templates, but there is a kind of hook implementation, called [[Create_an_ODT_document_template#Other_personalized_tags|substitution functions]].&lt;br /&gt;
&lt;br /&gt;
You can either create your own module, then create your own substitution function, but for the sake of simplicity in this tutorial, we will directly use the CustomFields substitution function. However, please remember that you will have to redo your changes everytime you will update the CustomFields module, so if you don&#039;t want to do that it would be better if you create your own module and substitution function.&lt;br /&gt;
&lt;br /&gt;
Open the file &#039;&#039;&#039;htdocs/customfields/core/substitutions/functions_customfields.lib.php&#039;&#039;&#039; and go to the function &#039;&#039;&#039;customfields_completesubstitutionarray_lines&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
At the very end of this function, but inside the customfields check:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) {&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
But just before the ending bracket, you should add the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($object-&amp;gt;table_element_line === &#039;facturedet&#039;) { // First check that we are in the right module, we don&#039;t want to use coefficient for other modules if we didn&#039;t define the custom field there&lt;br /&gt;
    $substitutionarray[&#039;line_price_full&#039;] = $substitutionarray[&#039;line_price_ttc&#039;] * $substitutionarray[&#039;cf_coefficient&#039;]; // multiply the total price with our coefficient&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code will add a new tag &#039;&#039;&#039;{line_price_full}&#039;&#039;&#039; that you can then use inside your ODT templates.&lt;br /&gt;
&lt;br /&gt;
Just place this tag in a table in your ODT document, between the table tags ( [!-- BEGIN row.lines --] ... [!-- END row.lines --] ), and this will print your full price.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in PDF templates ===&lt;br /&gt;
&lt;br /&gt;
First, we will create our own PDF template by copying an existing template, and then we will edit it to change the price of items.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Creating your own PDF template by copying an existing template&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Go into the following folder: &#039;&#039;&#039;htdocs/core/modules/facture/doc/&#039;&#039;&#039;, then copy the file &#039;&#039;&#039;pdf_crabe.modules.php&#039;&#039;&#039; and name the new one &#039;&#039;&#039;pdf_crabecoeff.modules.php&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Then open it in your favourite editor, and change the following lines:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures // change pdf_crabe into pdf_crabecoeff&lt;br /&gt;
{&lt;br /&gt;
...&lt;br /&gt;
    function __construct($db)&lt;br /&gt;
    {&lt;br /&gt;
        ...&lt;br /&gt;
	$this-&amp;gt;name = &amp;quot;crabe&amp;quot;; // change &amp;quot;crabe&amp;quot; into &amp;quot;crabecoeff&amp;quot;&lt;br /&gt;
        ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you have your own PDF template!&lt;br /&gt;
&lt;br /&gt;
Now you should &#039;&#039;&#039;enable your PDF template&#039;&#039;&#039; before doing anything else, by going into the Modules Administration panel, and into the Invoice admin page, then click on the ON button at the right of the &#039;&#039;&#039;crabecoeff&#039;&#039;&#039; entry to enable the template.&lt;br /&gt;
&lt;br /&gt;
Now, let&#039;s proceed onto modifying the prices.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Changing the prices automatically in your PDF&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
You can now do pretty much anything you want in your PDF template, but I will give you a mean to automatically change the prices of every products with our coefficient, as well as the total price, without editing too much the template.&lt;br /&gt;
&lt;br /&gt;
First, try to find the following code of line (should be inside the write_file() method):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf=pdf_getInstance($this-&amp;gt;format);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, just copy/paste the following code &#039;&#039;&#039;below&#039;&#039;&#039; pdf_getInstance():&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$this-&amp;gt;customfields = customfields_fill_object($object, null, $outputlangs, null, true); // beautified values&lt;br /&gt;
$this-&amp;gt;customfields_raw = customfields_fill_object($object, null, $outputlangs, &#039;raw&#039;, null); // raw values&lt;br /&gt;
$this-&amp;gt;customfields_lines = customfields_fill_object_lines($object, null, $outputlangs, null, true); // product lines&#039; values&lt;br /&gt;
&lt;br /&gt;
// Resetting every $object price because we will compute them anew&lt;br /&gt;
$object-&amp;gt;total_ht = 0;&lt;br /&gt;
$object-&amp;gt;total_tva = 0;&lt;br /&gt;
$object-&amp;gt;total_ttc = 0;&lt;br /&gt;
// For each line of product/service, we will recompute all the prices&lt;br /&gt;
foreach($object-&amp;gt;lines as $line) {&lt;br /&gt;
    // Get the coefficient custom field&#039;s value for the current line&lt;br /&gt;
    $coeff = $object-&amp;gt;customfields-&amp;gt;lines-&amp;gt;{$line-&amp;gt;rowid}-&amp;gt;cf_coefficient;&lt;br /&gt;
    // Recompute the price of this product/service line&lt;br /&gt;
    $line-&amp;gt;total_ht = $line-&amp;gt;total_ht*$coeff; // compute no tax price&lt;br /&gt;
    $line-&amp;gt;total_tva = $line-&amp;gt;total_tva*$coeff; // compute tax amount&lt;br /&gt;
    $line-&amp;gt;total_ttc = $line-&amp;gt;total_ttc*$coeff; // compute tax included price&lt;br /&gt;
    // Do the sum of all new products/services prices and tva (will be incremented with every product/service line)&lt;br /&gt;
    $object-&amp;gt;total_ht += $line-&amp;gt;total_ht; // global no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva += $line-&amp;gt;total_tva; // global vat/tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc += $line-&amp;gt;total_ttc; // global  tax included price&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you should try to generate your PDF document, and you should get something like this:&lt;br /&gt;
[[File:Cfe3.jpg]]&lt;br /&gt;
&lt;br /&gt;
Note: printing the coefficient as a column in your PDF document is also possible but will not be covered here because PDF templates will vary for every persons depending on your needs.&lt;br /&gt;
For guidelines about how to do this, you can read the documentation on [[Create_a_PDF_document_template|how to create a PDF template]] and [[Module_CustomFields#Implementing_in_PDF_templates|how to use custom fields in PDF templates]].&lt;br /&gt;
&lt;br /&gt;
Hint: you should be looking inside the following conditional block, inside the write_file() method:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
{&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
Unluckily there is not (yet) any hooking context to modify how lines of products/services are printed, thus the only thing you can do is directly edit the &#039;&#039;&#039;template file&#039;&#039;&#039; and manually put inside your code to load your custom field and print it the way you want using HTML formatting, for our example:&lt;br /&gt;
&lt;br /&gt;
* For Dolibarr 3.3 and above, edit the file &#039;&#039;&#039;htdocs/core/tpl/objectline_view.tpl.php&#039;&#039;&#039;&lt;br /&gt;
* For Dolibarr 3.1 and 3.2, edit the file &#039;&#039;&#039;htdocs/core/tpl/freeproductline_view.tpl.php&#039;&#039;&#039; AND &#039;&#039;&#039;predefinedproductline_view.tpl.php&#039;&#039;&#039; (apply the same changes to both files)&lt;br /&gt;
&lt;br /&gt;
In the template file you opened, find the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And replace it by:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = $line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = &#039;facturedet&#039;; // you can use $this-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
&lt;br /&gt;
// Modifying the HT price and printing it on the Dolibarr&#039;s datasheet interface&lt;br /&gt;
// Applying a multiplying/dividing coefficient to an HT price will give a mathematically correct TTC price since the taxes are also a multiplier&lt;br /&gt;
// However, if you want to apply other mathematical operations (like addition or substaction), you should use the total price with $line-&amp;gt;total_ttc&lt;br /&gt;
echo price($line-&amp;gt;total_ht * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient);&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should produce something like this:&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Technical note: here we are using customfields_fill_object() instead of customfields_fill_object_lines() (the latter being specifically adapted to lines and thus should theoretically be used here) for performance reasons because the template file we are editing is called in a loop, and customfields_fill_object_lines() should be called outside a loop (because it already does the job that is done by the loop), so here we &#039;tricks&#039; the customfields_fill_object() function to avoid this loop by submitting &#039;facturedet&#039; as a module by itself (when in fact it&#039;s a product lines table, but it works the same way nevertheless). This is a good demonstration of the flexibility of the CustomFields functions and methods.&lt;br /&gt;
&lt;br /&gt;
= Custom tax per product =&lt;br /&gt;
&lt;br /&gt;
This part will show you how to create a static custom tax per product. Static means that the tax will be stored per product, and not per product&#039;s line, thus you just have to setup the tax once per product and it will always be applied in every invoice you add this product.&lt;br /&gt;
&lt;br /&gt;
To do that, you will need the CustomFields Pro module + ProductsEasyAccess.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
1- Create a product&#039;s custom fields called &amp;quot;custom_tax&amp;quot;. Now, using ProductsEasyAccess, you can access this field wherever you wants, either to show it on the Dolibarr&#039;s interface (see previous chapter), or in a PDF template, which we will describe below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2- Modify your invoice&#039;s PDF template. You first need to load all the products&#039; fields &#039;&#039;before&#039;&#039; the loop over the products&#039; lines. To do this, search for a line like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// This should be placed above the &amp;quot;loop lines&amp;quot; below, add this:&lt;br /&gt;
&lt;br /&gt;
dol_include_once(&#039;/productseasyaccess/lib/productseasyaccess.lib.php&#039;);&lt;br /&gt;
fill_object_with_products_fields($object);&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines, search for this command&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  // Add this just below the for loop, this will ease your php manipulation after.&lt;br /&gt;
  // This will allow you to more easily access the products&#039; fields (this will store the id of each product inside $lineproductid).&lt;br /&gt;
  $line_product_id = $object-&amp;gt;lines[$i]-&amp;gt;fk_product;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4- You can now use your products&#039; fields in your PDF. To access the custom_tax field, just use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And to print it in your PDF:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell(0,3, $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax, 0, &#039;L&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5- Then, if you want to count the custom tax total and add it to your vat total, you can create a counter inside the for loop we cited in the first step:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_custom_tax = 0; // Initialize the custom tax total counter&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  $object-&amp;gt;total_custom_tax += $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then find inside the function **_tableau_tot**, find the following line:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And prepend the total modification like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc + $object-&amp;gt;total_custom_tax; // prepend this to add the custom tax to the total&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Done. This should show your custom tax per product and your total including all custom taxes.&lt;/div&gt;</summary>
		<author><name>Lrq3000</name></author>
	</entry>
	<entry>
		<id>https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=32932</id>
		<title>Module CustomFields Cases</title>
		<link rel="alternate" type="text/html" href="https://wiki.dolibarr.org/index.php?title=Module_CustomFields_Cases&amp;diff=32932"/>
		<updated>2014-11-25T17:46:30Z</updated>

		<summary type="html">&lt;p&gt;Lrq3000: /* Dynamic list selection based on another one */ added links&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Category:CustomFields]]&lt;br /&gt;
{{TemplateDocDevEn}}&lt;br /&gt;
{{TemplateModEN}}&lt;br /&gt;
{{BasculeDevUserEn|&lt;br /&gt;
name=CustomFields|&lt;br /&gt;
num=8500|&lt;br /&gt;
devdoc=[[Module_CustomFields_Dev]]|&lt;br /&gt;
userdoc=[[Module_CustomFields]]|}}&lt;br /&gt;
This page lists a compilation of concrete, practical examples uses of [[Module_CustomFields|CustomFields]] and of its various features.&lt;br /&gt;
&lt;br /&gt;
If you had an experience with CustomFields, you can also add here your case as an example for others to get inspired.&lt;br /&gt;
&lt;br /&gt;
= How to make a generic PDF template listing all custom fields =&lt;br /&gt;
&lt;br /&gt;
To make a generic PDF template listing all custom fields, you just need to take an existing PDF template, then you need to follow these 3 steps:&lt;br /&gt;
&lt;br /&gt;
1/ Edit the class name (required, else it will conflict with the original template).&lt;br /&gt;
&lt;br /&gt;
Eg: if we take the crabe template for invoices, we change:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;crabe&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
into:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_customfields extends ModelePDFFactures {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
  function __constructor($db) {&lt;br /&gt;
// ... php code ...&lt;br /&gt;
    $this-&amp;gt;name = &amp;quot;customfields&amp;quot;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2/ Copy this generic function at the end of the PDF:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propale/order/etc... (CustomFields simpler functions will automatically adapt)&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    global $conf;&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs); // set default PDF font size&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
    // Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
    $customfields = customfields_fill_object($object, null, $outputlangs, null, true);&lt;br /&gt;
&lt;br /&gt;
    // Setting the starting position of the text cursor&lt;br /&gt;
    $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
    $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
    // Printing the customfields&lt;br /&gt;
    foreach ($object-&amp;gt;customfields as $label=&amp;gt;$value) { // $value is already formatted!&lt;br /&gt;
        // Get translated label&lt;br /&gt;
        $translatedlabel = $customfields-&amp;gt;findLabelPDF($label, $outputlangs); // translated label of the customfield (not translated by default in customfields_fill_object() because a field should always be accessible by a base name, whatever the translation is)&lt;br /&gt;
&lt;br /&gt;
        // PDF formatting, placement and printing&lt;br /&gt;
        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
        $pdf-&amp;gt;MultiCell(0,3, $translatedlabel.&#039; ($object-&amp;gt;customfields-&amp;gt;&#039;.$label.&#039;): &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Alternative function (with a bit less functionalities, but more controllable):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
/**&lt;br /&gt;
 *   	\brief      Show the customfields in a new page (used to debug if CustomFields setup is correct)&lt;br /&gt;
 *   	\param      pdf     		PDF factory&lt;br /&gt;
 * 		\param		object			Object invoice/propal/product/whatever...&lt;br /&gt;
 *      \param      outputlangs		Object lang for output&lt;br /&gt;
 */&lt;br /&gt;
function _pagecustomfields(&amp;amp;$pdf,$object,$outputlangs)&lt;br /&gt;
{&lt;br /&gt;
    $default_font_size = pdf_getPDFFontSize($outputlangs);&lt;br /&gt;
&lt;br /&gt;
            if (empty($object-&amp;gt;table_element) or empty($object-&amp;gt;id)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;Current \$object is not compatible with CustomFields, could not find table_element or id.&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    // Init and main vars&lt;br /&gt;
    include_once(DOL_DOCUMENT_ROOT.&#039;/customfields/class/customfields.class.php&#039;);&lt;br /&gt;
    $customfields = new CustomFields($this-&amp;gt;db, $object-&amp;gt;table_element);&lt;br /&gt;
&lt;br /&gt;
            // Fetching custom fields records&lt;br /&gt;
            $fields = $customfields-&amp;gt;fetch($object-&amp;gt;id);&lt;br /&gt;
&lt;br /&gt;
            if (!isset($fields)) {&lt;br /&gt;
                $pdf-&amp;gt;MultiCell(0,3, &amp;quot;No custom field could be found for this object. Please check your configuration (did you create at least one customfield and set a value in the current datasheet?)&amp;quot;, 0, &#039;L&#039;);&lt;br /&gt;
                return 1;&lt;br /&gt;
            } else {&lt;br /&gt;
                // Setting the starting position of the text cursor&lt;br /&gt;
                $pdf-&amp;gt;SetXY($this-&amp;gt;page_largeur - $this-&amp;gt;marge_droite - ($pdf-&amp;gt;GetStringWidth($titre) + 3), $pdf-&amp;gt;GetY()+4);&lt;br /&gt;
                $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1);&lt;br /&gt;
&lt;br /&gt;
                // Printing the customfields&lt;br /&gt;
                foreach ($fields as $key=&amp;gt;$field) {&lt;br /&gt;
                        $translatedname = $customfields-&amp;gt;findLabelPDF($key, $outputlangs); // label of the customfield&lt;br /&gt;
                        $value = $customfields-&amp;gt;simpleprintFieldPDF($key, $field, $outputlangs); // value (cleaned and properly formatted) of the customfield&lt;br /&gt;
&lt;br /&gt;
                        $pdf-&amp;gt;SetFont(&#039;&#039;,&#039;B&#039;, $default_font_size);&lt;br /&gt;
                        $pdf-&amp;gt;MultiCell(0,3, $translatedname.&#039;: &#039;.$value, 0, &#039;L&#039;); // printing the customfield&lt;br /&gt;
                        $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY()+1); // line return for the next printing&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
    return 1;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3/ Call the _pagecustomfields function at the right place (generally, after _pagefoot and just before $pdf-&amp;gt;Close()):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Pied de page&lt;br /&gt;
$this-&amp;gt;_pagefoot($pdf,$object,$outputlangs);&lt;br /&gt;
$pdf-&amp;gt;AliasNbPages();&lt;br /&gt;
&lt;br /&gt;
// CustomFields&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) { // if the customfields module is activated...&lt;br /&gt;
        // Add a page with the customfields&lt;br /&gt;
        $pdf-&amp;gt;AddPage();&lt;br /&gt;
        $this-&amp;gt;_pagecustomfields($pdf,$object,$outputlangs);&lt;br /&gt;
        $pagenb++;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
$pdf-&amp;gt;Close(); // you should first search for this, you should only find one occurrence&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then go to the module&#039;s admin panel (eg: invoice admin panel) and enable the template you&#039;ve just created.&lt;br /&gt;
&lt;br /&gt;
You can now use it to test if your customfields work.&lt;br /&gt;
&lt;br /&gt;
= Conditions on fields selection =&lt;br /&gt;
&lt;br /&gt;
Sometimes you may want to show a DropdownBox or Constraint (or another multi-choice type), but you may want to show or hide choices based on conditions (eg: only if it&#039;s a prospect, only if this zone was selected, only if this category was selected, etc..).&lt;br /&gt;
&lt;br /&gt;
To know which is the best solution you should choose to implement your condition, you should first know in which category your condition falls in.&lt;br /&gt;
&lt;br /&gt;
== Conditions categories ==&lt;br /&gt;
&lt;br /&gt;
Conditions can be categorized into 4 types:&lt;br /&gt;
&lt;br /&gt;
* static condition: choices are restrained on a condition that never changes (eg: only show third-parties that are suppliers, or lowercase all data on sql insertion, or check that a field is above a certain number, etc.).&lt;br /&gt;
* semi-dynamic condition: choices that are constrained on a static table but based on the value of another field/customfield. This is also commonly called &amp;quot;Cascaded dropdown lists&amp;quot; or &amp;quot;Dynamically linked dropdown lists&amp;quot;.&lt;br /&gt;
* dynamic: everything is dynamic: choices are based on a subfield of a field of the current module, so that there&#039;s no material table and it&#039;s impossible to create a view (because you would need to create one view per id) (eg: show only the contacts attached to the third-party being accessed)&lt;br /&gt;
* timed condition: a condition that is based on time or recurrence (eg: at 5:00 AM delete old records, etc..)&lt;br /&gt;
&lt;br /&gt;
== Conditions solutions ==&lt;br /&gt;
&lt;br /&gt;
Now that you know what kind of condition you want to set, you can choose one of the following solutions depending on your preferences and knowledge.&lt;br /&gt;
&lt;br /&gt;
In any case, you can always use the &#039;&#039;&#039;CustomFields&#039;s overload functions&#039;&#039;&#039; to fit your needs for nearly every type of condition.&lt;br /&gt;
&lt;br /&gt;
=== Static condition ===&lt;br /&gt;
&lt;br /&gt;
* View: create a view on the table based on the condition, and then create a custom field constrained on this view (which is just like any table), eg: only show third-parties that are suppliers:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
CREATE VIEW llx_societe_supplier_view AS&lt;br /&gt;
SELECT *&lt;br /&gt;
FROM llx_societe&lt;br /&gt;
WHERE fournisseur=1;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
WARNING: foreign keys (and thus constraints) can&#039;t be used on views with MySQL, and materialized views neither exist, so you can&#039;t use this method with MySQL.&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
* SQL Transformations&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
=== Semi-dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade option is ideal in most cases, and if not enough, you can use the Cascade Custom option and Overloading Functions.&lt;br /&gt;
* View&lt;br /&gt;
* Check&lt;br /&gt;
* Trigger&lt;br /&gt;
&lt;br /&gt;
=== Dynamic condition ===&lt;br /&gt;
&lt;br /&gt;
* CustomFields&#039;s Cascade Custom and Overload functions&lt;br /&gt;
* Making your own module, which calls the CustomFields class&lt;br /&gt;
&lt;br /&gt;
=== Timed condition ===&lt;br /&gt;
&lt;br /&gt;
* SQL scheduled events&lt;br /&gt;
* Cron job&lt;br /&gt;
* CustomFields&#039;s overload functions&lt;br /&gt;
&lt;br /&gt;
= Overloading functions =&lt;br /&gt;
&lt;br /&gt;
Here are a few concrete use cases of overloading functions to achieve various goals.&lt;br /&gt;
&lt;br /&gt;
== Dynamic list selection based on another one ==&lt;br /&gt;
&lt;br /&gt;
The goal is to show two custom fields on the Third-Party module: one which gives the zone (secteur) of the third-party (Isere, Alpes du sud, Haute-Savoie...) and the other which gives relative to the zone the list of all the ski resorts (station_a) inside the selected zone.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will show how to manually manage this kind of requirements using [[Module_CustomFields#Overloading_functions:_Adding_your_own_management_code_for_custom_fields|overloading functions]], but in latest CustomFields releases, the same solution can be achieved automatically using the [[Module_CustomFields#Cascade|Cascade option]].&lt;br /&gt;
&lt;br /&gt;
We have one table &#039;&#039;&#039;llx_k_station&#039;&#039;&#039; (rowid, station (char50), secteur(char50)) which contains the following: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1,les 2 alpes,isère&lt;br /&gt;
2,chamrousse,isère&lt;br /&gt;
3,alpe d&#039;huez,isère&lt;br /&gt;
4,vars,alpes du sud&lt;br /&gt;
5,risoul,alpes du sud&lt;br /&gt;
etc...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In CustomFields&#039;s admin panel, we create two custom fields:&lt;br /&gt;
&lt;br /&gt;
1- secteur: returns the list of all zones - type DropdownBox: enum(&#039;- Aucun&#039;,&#039;Alpes du Sud&#039;,&#039;export&#039;,&#039;Haute-Savoie&#039;,&#039;Isère&#039;, etc...)&lt;br /&gt;
&lt;br /&gt;
2- station_a: returns the list of all ski resorts - type constraint on &#039;&#039;&#039;llx_k_station&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Here is the code to put in customfields_fields_extend.lib.php that will allow to show only the ski resorts corresponding to the selected zone:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_editview_societe_station_a (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$parameters, &amp;amp;$action, &amp;amp;$user, &amp;amp;$idvar, &amp;amp;$rightok, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
  global $db; // allow to access database functions&lt;br /&gt;
  $sql=&amp;quot;SELECT llx_k_station.station, llx_k_station.rowid FROM llx_societe_customfields INNER JOIN llx_k_station ON llx_societe_customfields.Secteur = llx_k_station.secteur WHERE llx_societe_customfields.fk_societe=&amp;quot;.$object-&amp;gt;id;&lt;br /&gt;
$result=$db-&amp;gt;query($sql);&lt;br /&gt;
 &lt;br /&gt;
  if ($result) {&lt;br /&gt;
    $num = $db-&amp;gt;num_rows($result);&lt;br /&gt;
    $i = 0;&lt;br /&gt;
    $myarray = array();&lt;br /&gt;
    while ($i &amp;lt; $num)&lt;br /&gt;
    {&lt;br /&gt;
      $obj = $db-&amp;gt;fetch_object($result);&lt;br /&gt;
      $myarray []=array(&#039;id&#039;=&amp;gt;$obj-&amp;gt;rowid, &#039;value&#039;=&amp;gt;$obj-&amp;gt;station);&lt;br /&gt;
      $i++;&lt;br /&gt;
    }&lt;br /&gt;
    $value = $myarray; &lt;br /&gt;
    $db-&amp;gt;free($result);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to manub for giving this case.&lt;br /&gt;
&lt;br /&gt;
== Dynamic discount computation per products lines on propales ==&lt;br /&gt;
&lt;br /&gt;
The goal here is to compute the discount for a product line in propales based on a computation on three custom fields.&lt;br /&gt;
&lt;br /&gt;
First, create three custom fields for &amp;quot;Line commercial proposals&amp;quot;: vl, cpl and lc.&lt;br /&gt;
&lt;br /&gt;
Then add the following overloading function at the end of your customfields_fields_extend.lib.php file (read the comments for more informations):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_propaldet_vl (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
&lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $vl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;vl&#039;};&lt;br /&gt;
    $cpl = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;cpl&#039;};&lt;br /&gt;
    $lc = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;lc&#039;};&lt;br /&gt;
&lt;br /&gt;
    // Compute the discount&lt;br /&gt;
    $remise_calculee = (100 - $vl) * (100 - $cpl) * (100 - $lc);&lt;br /&gt;
&lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $remise_calculee = price2num($remise_calculee);&lt;br /&gt;
&lt;br /&gt;
    // Assign the new discount into the PropalLine object&lt;br /&gt;
    $object-&amp;gt;remise_percent = $remise_calculee;&lt;br /&gt;
&lt;br /&gt;
    // Update the price (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
&lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
&lt;br /&gt;
    //$q = &amp;quot;UPDATE  &amp;quot;.MAIN_DB_PREFIX.&amp;quot;propaldet SET remise_percent=&amp;quot;.$remise_calculee.&amp;quot; WHERE  rowid=&amp;quot;.$object-&amp;gt;rowid;&lt;br /&gt;
    //$db-&amp;gt;query($q);&lt;br /&gt;
    &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;vl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;cpl&amp;quot;]);&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;lc&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Linking Dolibarr objects from two different modules =&lt;br /&gt;
&lt;br /&gt;
Dolibarr sometimes offers the possibility to link two different modules. Eg: when you convert an Intervention card into an Invoice.&lt;br /&gt;
&lt;br /&gt;
In this case, you might want in your templates to fetch the custom fields of the linked object. Eg: you generate a PDF from the Invoice, but you want to get the custom fields from the Intervention that was converted into the Invoice.&lt;br /&gt;
&lt;br /&gt;
The key here is to use the &#039;&#039;&#039;llx_element_element&#039;&#039;&#039; table or the &#039;&#039;&#039;llx_element_*&#039;&#039;&#039; tables (eg: llx_element_contact): this is a standard Dolibarr table that stores all links between objects of two different modules.&lt;br /&gt;
&lt;br /&gt;
Here is the structure:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
rowid | fk_source | sourcetype | fk_target | targettype&lt;br /&gt;
-------------------------------------------------------&lt;br /&gt;
1 | 2 | fichinter | 5 | facture&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can then use a php code like the following to populate your $object with customfields:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
// This will also create and returns us an instance of the CustomFields class, which we will use to manually query the llx_element_element table&lt;br /&gt;
$customfields = customfields_fill_object($object, null, $outputlangs, &#039;facture&#039;, true); // beautified values&lt;br /&gt;
&lt;br /&gt;
// Querying the llx_element_element table manually to get the link between the invoice and the linked intervention card (we want the id of the intervention card)&lt;br /&gt;
// fetchAny($columns, $table, $where=&#039;&#039;, $orderby=&#039;&#039;, $limitby=&#039;&#039;)&lt;br /&gt;
$eltrow = $customfields-&amp;gt;fetchAny(&#039;*&#039;, MAIN_DB_PREFIX.&#039;element_element&#039;, &#039;targettype=&amp;quot;&#039;.$object-&amp;gt;element.&#039;&amp;quot; and fk_target=&#039;.$object-&amp;gt;id.&#039; and sourcetype=&amp;quot;fichinter&amp;quot;&#039;);&lt;br /&gt;
&lt;br /&gt;
// Save the intervention&#039;s id&lt;br /&gt;
$sourceid = $eltrow[0][&#039;fk_source&#039;];&lt;br /&gt;
&lt;br /&gt;
// Crafting a dummy intervention object&lt;br /&gt;
$fromobject = object(); // Init the object&lt;br /&gt;
$fromobject-&amp;gt;id = $sourceid; // We need the id and..&lt;br /&gt;
$fromobject-&amp;gt;table_element = &#039;fichinter&#039;; // the table_element (name of the table)&lt;br /&gt;
&lt;br /&gt;
// We can then fill $object with the intervention custom fields (we store them in a sub property &#039;fichinter&#039; to avoid conflicts with invoice&#039;s custom fields)&lt;br /&gt;
customfields_fill_object($object, $fromobject, $outputlangs, &#039;fichinter&#039;, true);&lt;br /&gt;
&lt;br /&gt;
// Now you can access invoices custom fields using:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;facture-&amp;gt;cf_myfield);&lt;br /&gt;
// And access intervention custom fields with:&lt;br /&gt;
print($object-&amp;gt;customfields-&amp;gt;fichinter-&amp;gt;cf_myfield);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Thank&#039;s to netassopro for the tip.&lt;br /&gt;
&lt;br /&gt;
= Modify the total price of every product/service line with a coefficient (without tampering the discount) =&lt;br /&gt;
&lt;br /&gt;
Here we will go through the creation and management of a custom field called &#039;&#039;&#039;coefficient&#039;&#039;&#039; that will change the total price of each product by modifying it by the coefficient.&lt;br /&gt;
&lt;br /&gt;
NOTE: this will not create a coefficient per product but rather per product&#039;s line. Thus if you want to create a coefficient that never change between invoices but only depending on the product and stays static, please refer to the next chapter below about creating a custom tax per product.&lt;br /&gt;
&lt;br /&gt;
The preliminary and necessary step is to first create a custom field that will be used to modify the total price. We will create one for Client Invoice Lines.&lt;br /&gt;
&lt;br /&gt;
Go to the CustomFields administration panel, then into the Client Invoice Lines tab, then click on the button New Field, name the field &#039;&#039;&#039;coefficient&#039;&#039;&#039;, and set the type to &#039;&#039;&#039;Double&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
When you go to client invoices in Dolibarr, you should then see something similar to this:&lt;br /&gt;
[[File:Cfe1.jpg]]&lt;br /&gt;
You can see that our coefficient field was successfully created.&lt;br /&gt;
&lt;br /&gt;
Then here are two alternative ways of implementing the change of the total price of each product relativery to this coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change total price in database ==&lt;br /&gt;
&lt;br /&gt;
Here we will use a &amp;quot;save&amp;quot; overloading function to change the total price directly in the database, thus we won&#039;t have to modify anything else (eg: in PDT or ODT) since the total price will be already updated in the database.&lt;br /&gt;
&lt;br /&gt;
Warning: this method will work only with CustomFields =&amp;gt; 3.2.23 since the &amp;quot;save&amp;quot; overloading function did not work for products lines before.&lt;br /&gt;
&lt;br /&gt;
Just add the following overloading function in your customfields_fields_extend.lib.php file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
function customfields_field_save_facturedet_coefficient (&amp;amp;$currentmodule, &amp;amp;$object, &amp;amp;$action, &amp;amp;$user, &amp;amp;$customfields, &amp;amp;$field, &amp;amp;$name, &amp;amp;$value) {&lt;br /&gt;
    global $db; // allow to access database functions&lt;br /&gt;
 &lt;br /&gt;
    // Fetch the new posted values&lt;br /&gt;
    // All $_POST data are generically appended into the $object properties&lt;br /&gt;
    $coefficient = $object-&amp;gt;{$customfields-&amp;gt;varprefix.&#039;coefficient&#039;}; // you could also use $coefficient = $value;&lt;br /&gt;
 &lt;br /&gt;
    // Clean parameters&lt;br /&gt;
    include_once DOL_DOCUMENT_ROOT.&#039;/core/lib/price.lib.php&#039;;&lt;br /&gt;
    $coefficient = price2num($coefficient);&lt;br /&gt;
 &lt;br /&gt;
    // Get the original subprices (it&#039;s not automatic when we edit remise_percent and update, we have to recompute manually the prices before calling $object-&amp;gt;update())&lt;br /&gt;
    global $mysoc;&lt;br /&gt;
    $localtaxes_type=getLocalTaxesFromRate($object-&amp;gt;tva_tx,0,$mysoc);&lt;br /&gt;
    $tabprice=calcul_price_total($object-&amp;gt;qty, $object-&amp;gt;subprice, $object-&amp;gt;remise_percent, $object-&amp;gt;tva_tx, $object-&amp;gt;localtax1_tx, $object-&amp;gt;localtax2_tx, 0, &#039;HT&#039;, $object-&amp;gt;info_bits, $object-&amp;gt;type, &#039;&#039;, $localtaxes_type);&lt;br /&gt;
    $object-&amp;gt;total_ht  = $tabprice[0];&lt;br /&gt;
    $object-&amp;gt;total_tva = $tabprice[1];&lt;br /&gt;
    $object-&amp;gt;total_ttc = $tabprice[2];&lt;br /&gt;
    $object-&amp;gt;total_localtax1 = $tabprice[9];&lt;br /&gt;
    $object-&amp;gt;total_localtax2 = $tabprice[10];&lt;br /&gt;
 &lt;br /&gt;
    // Update the subprices with the coefficient&lt;br /&gt;
    $object-&amp;gt;total_ht = $object-&amp;gt;total_ht * $coefficient; // compute no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva = $object-&amp;gt;total_tva * $coefficient; // compute tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc * $coefficient; // compute tax included price&lt;br /&gt;
 &lt;br /&gt;
    // Update into database&lt;br /&gt;
    $object-&amp;gt;update(&#039;&#039;,true); // true to disable the trigger, else it will recall customfields, which will call this overloading function, etc. in an infinite loop.&lt;br /&gt;
    $object-&amp;gt;update_total();&lt;br /&gt;
 &lt;br /&gt;
    // Finally, cleanup POST data to avoid conflicts next time, reinserting the same discount. Else the fields will remember these values and it may mistakenly reuse the same values.&lt;br /&gt;
    unset($_POST[$customfields-&amp;gt;varprefix.&amp;quot;coefficient&amp;quot;]);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should show the correct total price on the Dolibarr&#039;s interface without modifying the unit subprice (P.U. Unit):&lt;br /&gt;
&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
You have nothing to edit in your PDF or ODT template, since the new subprice with applied coefficient will be automatically printed instead of the old one without coefficient.&lt;br /&gt;
&lt;br /&gt;
== Change printed total price per product without tampering the subprice in the database ==&lt;br /&gt;
&lt;br /&gt;
Here is a way to change the printed total price (the one shown on screen and in PDF/ODT) but without changing the total price in the database.&lt;br /&gt;
&lt;br /&gt;
The following sub-chapters are all independent, thus you can only follow the ones that you require, eg: if you only need to change the price in your PDF documents, just create a custom field then jump to the sub-chapter to &#039;&#039;&#039;Implement the change of price in PDF&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in ODT templates ===&lt;br /&gt;
&lt;br /&gt;
There&#039;s no eval implementation currently inside ODT templates, but there is a kind of hook implementation, called [[Create_an_ODT_document_template#Other_personalized_tags|substitution functions]].&lt;br /&gt;
&lt;br /&gt;
You can either create your own module, then create your own substitution function, but for the sake of simplicity in this tutorial, we will directly use the CustomFields substitution function. However, please remember that you will have to redo your changes everytime you will update the CustomFields module, so if you don&#039;t want to do that it would be better if you create your own module and substitution function.&lt;br /&gt;
&lt;br /&gt;
Open the file &#039;&#039;&#039;htdocs/customfields/core/substitutions/functions_customfields.lib.php&#039;&#039;&#039; and go to the function &#039;&#039;&#039;customfields_completesubstitutionarray_lines&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
At the very end of this function, but inside the customfields check:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($conf-&amp;gt;global-&amp;gt;MAIN_MODULE_CUSTOMFIELDS) {&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
But just before the ending bracket, you should add the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
if ($object-&amp;gt;table_element_line === &#039;facturedet&#039;) { // First check that we are in the right module, we don&#039;t want to use coefficient for other modules if we didn&#039;t define the custom field there&lt;br /&gt;
    $substitutionarray[&#039;line_price_full&#039;] = $substitutionarray[&#039;line_price_ttc&#039;] * $substitutionarray[&#039;cf_coefficient&#039;]; // multiply the total price with our coefficient&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code will add a new tag &#039;&#039;&#039;{line_price_full}&#039;&#039;&#039; that you can then use inside your ODT templates.&lt;br /&gt;
&lt;br /&gt;
Just place this tag in a table in your ODT document, between the table tags ( [!-- BEGIN row.lines --] ... [!-- END row.lines --] ), and this will print your full price.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in PDF templates ===&lt;br /&gt;
&lt;br /&gt;
First, we will create our own PDF template by copying an existing template, and then we will edit it to change the price of items.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Creating your own PDF template by copying an existing template&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Go into the following folder: &#039;&#039;&#039;htdocs/core/modules/facture/doc/&#039;&#039;&#039;, then copy the file &#039;&#039;&#039;pdf_crabe.modules.php&#039;&#039;&#039; and name the new one &#039;&#039;&#039;pdf_crabecoeff.modules.php&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Then open it in your favourite editor, and change the following lines:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
class pdf_crabe extends ModelePDFFactures // change pdf_crabe into pdf_crabecoeff&lt;br /&gt;
{&lt;br /&gt;
...&lt;br /&gt;
    function __construct($db)&lt;br /&gt;
    {&lt;br /&gt;
        ...&lt;br /&gt;
	$this-&amp;gt;name = &amp;quot;crabe&amp;quot;; // change &amp;quot;crabe&amp;quot; into &amp;quot;crabecoeff&amp;quot;&lt;br /&gt;
        ...&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you have your own PDF template!&lt;br /&gt;
&lt;br /&gt;
Now you should &#039;&#039;&#039;enable your PDF template&#039;&#039;&#039; before doing anything else, by going into the Modules Administration panel, and into the Invoice admin page, then click on the ON button at the right of the &#039;&#039;&#039;crabecoeff&#039;&#039;&#039; entry to enable the template.&lt;br /&gt;
&lt;br /&gt;
Now, let&#039;s proceed onto modifying the prices.&lt;br /&gt;
&lt;br /&gt;
==== &#039;&#039;&#039;Changing the prices automatically in your PDF&#039;&#039;&#039; ====&lt;br /&gt;
&lt;br /&gt;
You can now do pretty much anything you want in your PDF template, but I will give you a mean to automatically change the prices of every products with our coefficient, as well as the total price, without editing too much the template.&lt;br /&gt;
&lt;br /&gt;
First, try to find the following code of line (should be inside the write_file() method):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf=pdf_getInstance($this-&amp;gt;format);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now, just copy/paste the following code &#039;&#039;&#039;below&#039;&#039;&#039; pdf_getInstance():&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Init and main vars for CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$this-&amp;gt;customfields = customfields_fill_object($object, null, $outputlangs, null, true); // beautified values&lt;br /&gt;
$this-&amp;gt;customfields_raw = customfields_fill_object($object, null, $outputlangs, &#039;raw&#039;, null); // raw values&lt;br /&gt;
$this-&amp;gt;customfields_lines = customfields_fill_object_lines($object, null, $outputlangs, null, true); // product lines&#039; values&lt;br /&gt;
&lt;br /&gt;
// Resetting every $object price because we will compute them anew&lt;br /&gt;
$object-&amp;gt;total_ht = 0;&lt;br /&gt;
$object-&amp;gt;total_tva = 0;&lt;br /&gt;
$object-&amp;gt;total_ttc = 0;&lt;br /&gt;
// For each line of product/service, we will recompute all the prices&lt;br /&gt;
foreach($object-&amp;gt;lines as $line) {&lt;br /&gt;
    // Get the coefficient custom field&#039;s value for the current line&lt;br /&gt;
    $coeff = $object-&amp;gt;customfields-&amp;gt;lines-&amp;gt;{$line-&amp;gt;rowid}-&amp;gt;cf_coefficient;&lt;br /&gt;
    // Recompute the price of this product/service line&lt;br /&gt;
    $line-&amp;gt;total_ht = $line-&amp;gt;total_ht*$coeff; // compute no tax price&lt;br /&gt;
    $line-&amp;gt;total_tva = $line-&amp;gt;total_tva*$coeff; // compute tax amount&lt;br /&gt;
    $line-&amp;gt;total_ttc = $line-&amp;gt;total_ttc*$coeff; // compute tax included price&lt;br /&gt;
    // Do the sum of all new products/services prices and tva (will be incremented with every product/service line)&lt;br /&gt;
    $object-&amp;gt;total_ht += $line-&amp;gt;total_ht; // global no tax price&lt;br /&gt;
    $object-&amp;gt;total_tva += $line-&amp;gt;total_tva; // global vat/tax amount&lt;br /&gt;
    $object-&amp;gt;total_ttc += $line-&amp;gt;total_ttc; // global  tax included price&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s it, now you should try to generate your PDF document, and you should get something like this:&lt;br /&gt;
[[File:Cfe3.jpg]]&lt;br /&gt;
&lt;br /&gt;
Note: printing the coefficient as a column in your PDF document is also possible but will not be covered here because PDF templates will vary for every persons depending on your needs.&lt;br /&gt;
For guidelines about how to do this, you can read the documentation on [[Create_a_PDF_document_template|how to create a PDF template]] and [[Module_CustomFields#Implementing_in_PDF_templates|how to use custom fields in PDF templates]].&lt;br /&gt;
&lt;br /&gt;
Hint: you should be looking inside the following conditional block, inside the write_file() method:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
{&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Implementing the change of price in the Dolibarr interface ===&lt;br /&gt;
&lt;br /&gt;
Unluckily there is not (yet) any hooking context to modify how lines of products/services are printed, thus the only thing you can do is directly edit the &#039;&#039;&#039;template file&#039;&#039;&#039; and manually put inside your code to load your custom field and print it the way you want using HTML formatting, for our example:&lt;br /&gt;
&lt;br /&gt;
* For Dolibarr 3.3 and above, edit the file &#039;&#039;&#039;htdocs/core/tpl/objectline_view.tpl.php&#039;&#039;&#039;&lt;br /&gt;
* For Dolibarr 3.1 and 3.2, edit the file &#039;&#039;&#039;htdocs/core/tpl/freeproductline_view.tpl.php&#039;&#039;&#039; AND &#039;&#039;&#039;predefinedproductline_view.tpl.php&#039;&#039;&#039; (apply the same changes to both files)&lt;br /&gt;
&lt;br /&gt;
In the template file you opened, find the following code:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php echo price($line-&amp;gt;total_ht); ?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And replace it by:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Include the facade API of CustomFields&lt;br /&gt;
dol_include_once(&#039;/customfields/lib/customfields_aux.lib.php&#039;);&lt;br /&gt;
&lt;br /&gt;
// Filling the $object with customfields (you can then access customfields by doing $object-&amp;gt;customfields-&amp;gt;cf_yourfield)&lt;br /&gt;
$linecf = new stdClass();&lt;br /&gt;
$linecf-&amp;gt;id = $line-&amp;gt;rowid;&lt;br /&gt;
$linecf-&amp;gt;table_element = &#039;facturedet&#039;; // you can use $this-&amp;gt;table_element_line here instead of &#039;facturedet&#039; if you want this feature to work for every module supported by CustomFields&lt;br /&gt;
$customfields = customfields_fill_object($linecf);&lt;br /&gt;
&lt;br /&gt;
// Modifying the HT price and printing it on the Dolibarr&#039;s datasheet interface&lt;br /&gt;
// Applying a multiplying/dividing coefficient to an HT price will give a mathematically correct TTC price since the taxes are also a multiplier&lt;br /&gt;
// However, if you want to apply other mathematical operations (like addition or substaction), you should use the total price with $line-&amp;gt;total_ttc&lt;br /&gt;
echo price($line-&amp;gt;total_ht * $linecf-&amp;gt;customfields-&amp;gt;cf_coefficient);&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This should produce something like this:&lt;br /&gt;
[[File:Cfe2.jpg]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Technical note: here we are using customfields_fill_object() instead of customfields_fill_object_lines() (the latter being specifically adapted to lines and thus should theoretically be used here) for performance reasons because the template file we are editing is called in a loop, and customfields_fill_object_lines() should be called outside a loop (because it already does the job that is done by the loop), so here we &#039;tricks&#039; the customfields_fill_object() function to avoid this loop by submitting &#039;facturedet&#039; as a module by itself (when in fact it&#039;s a product lines table, but it works the same way nevertheless). This is a good demonstration of the flexibility of the CustomFields functions and methods.&lt;br /&gt;
&lt;br /&gt;
= Custom tax per product =&lt;br /&gt;
&lt;br /&gt;
This part will show you how to create a static custom tax per product. Static means that the tax will be stored per product, and not per product&#039;s line, thus you just have to setup the tax once per product and it will always be applied in every invoice you add this product.&lt;br /&gt;
&lt;br /&gt;
To do that, you will need the CustomFields Pro module + ProductsEasyAccess.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
1- Create a product&#039;s custom fields called &amp;quot;custom_tax&amp;quot;. Now, using ProductsEasyAccess, you can access this field wherever you wants, either to show it on the Dolibarr&#039;s interface (see previous chapter), or in a PDF template, which we will describe below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
2- Modify your invoice&#039;s PDF template. You first need to load all the products&#039; fields &#039;&#039;before&#039;&#039; the loop over the products&#039; lines. To do this, search for a line like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// This should be placed above the &amp;quot;loop lines&amp;quot; below, add this:&lt;br /&gt;
&lt;br /&gt;
dol_include_once(&#039;/productseasyaccess/lib/productseasyaccess.lib.php&#039;);&lt;br /&gt;
fill_object_with_products_fields($object);&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines, search for this command&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  // Add this just below the for loop, this will ease your php manipulation after.&lt;br /&gt;
  // This will allow you to more easily access the products&#039; fields (this will store the id of each product inside $lineproductid).&lt;br /&gt;
  $line_product_id = $object-&amp;gt;lines[$i]-&amp;gt;fk_product;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
4- You can now use your products&#039; fields in your PDF. To access the custom_tax field, just use:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And to print it in your PDF:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell(0,3, $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax, 0, &#039;L&#039;);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
5- Then, if you want to count the custom tax total and add it to your vat total, you can create a counter inside the for loop we cited in the first step:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_custom_tax = 0; // Initialize the custom tax total counter&lt;br /&gt;
&lt;br /&gt;
// Loop on each lines&lt;br /&gt;
&lt;br /&gt;
for ($i = 0 ; $i &amp;lt; $nblignes ; $i++)&lt;br /&gt;
&lt;br /&gt;
  $object-&amp;gt;total_custom_tax += $object-&amp;gt;productslines-&amp;gt;$line_product_id-&amp;gt;cf_custom_tax;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then find inside the function **_tableau_tot**, find the following line:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And prepend the total modification like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$object-&amp;gt;total_ttc = $object-&amp;gt;total_ttc + $object-&amp;gt;total_custom_tax; // prepend this to add the custom tax to the total&lt;br /&gt;
$pdf-&amp;gt;MultiCell($largcol2, $tab2_hl, price($sign * $object-&amp;gt;total_ttc, 0, $outputlangs), $useborder, &#039;R&#039;, 1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Done. This should show your custom tax per product and your total including all custom taxes.&lt;/div&gt;</summary>
		<author><name>Lrq3000</name></author>
	</entry>
</feed>