The CoreBOS Field Dependency Business Mapping is a mechanism that allows you to define dependencies between fields in different modules within the CoreBOS CRM system. It enables you to establish relationships and dependencies among fields, which determine their visibility and availability based on the values of other fields.
By defining field dependencies using the coreBOS Field Dependency Business Mapping, you can configure complex relationships between fields in different modules. This helps to enforce data integrity, streamline user interactions, and ensure that the right fields are available at the right time based on specific conditions.
The mapping is typically used in customizations and extensions of the CoreBOS CRM system to tailor the behavior of fields and modules according to specific business requirements and workflows.
The mapping is defined using an XML format. Here's an abstraction of the structure:
<map>
<originmodule>
<originname>Module_Name</originname>
</originmodule>
<dependencies>
<dependency>
<mode></mode> <!-- optional -->
<field>Field_Name</field>
<conditions>
<!-- Define the conditions for the field dependency -->
<condition>[...]</condition>
...
</conditions>
<actions>
<!-- Define the actions to be performed when the conditions are met -->
<mode></mode> <!-- optional -->
...
</actions>
</dependency>
...
</dependencies>
</map>
Now let's break down the structure:
<map>
: The root element that encapsulates the entire mapping.<originmodule>
: Specifies the module where the field dependencies originate.<originname>
: Specifies the name of the module for which field dependencies are being defined.<dependencies>
: Contains the list of field dependencies within the module.<dependency>
: Represents a specific field dependency.<field>
: Specifies the name of the field for which the dependency is being defined.<conditions>
: Contains the conditions that determine when the field dependency is triggered.<condition>
: Defines a specific condition for the field dependency.<actions>
: Contains the actions to be performed when the conditions are met.Within the <conditions>
element, you can define one or more <condition>
elements that specify the conditions for the field dependency. These conditions can include various comparisons, logical operators, and values.
Similarly, within the <actions>
element, you can define the actions to be performed when the conditions are met. These actions can include showing or hiding fields, setting field values, executing workflows, invoking custom functions, and more.
This type of map works both in edit and details view, permiting you to define dependencies between fields. For example, it will permit you to make a field read-only (not editable) or not depending on the value selected in a given field or to change the available values in a picklist depending on the value.
The goal of this mapping is to define a set of rules/conditions and actions to be applied while editing a field in a coreBOS form.
The trigger event will be the onChange
HTML event. Whenever a field changes its’ value in a form we will evaluate a given set of conditions and if they are true we will apply a set of actions.
Since we will be evaluating the field on a per-module basis and to avoid additional time looking for related values via AJAX, the conditions will be evaluated using only information located on the form.
The supported condition operators are the same ones we support in the custom view filter system (see Conditional Popup and Popup Open Hook:
Group conditions can be created concatenating with the typical AND/OR operators. We will be using a similar concept to the one used for filters:
"groupid":"number that identifies the group of conditions",
"columnname":"coreBOS column identifier or simply the column/field name"
"comparator":"comparison operator"
"value":"text to look"
"columncondition":"and|or" (logical operator to join with the next condition)
The actions supported are:
This looks something like this:
<map>
<originmodule>
<originname>SalesOrder</originname>
</originmodule>
<dependencies>
<dependency>
<field>campaignname</field>
<condition>[{...}]</condition>
<actions>
<change>
<field>sponsor</field>
<value>Sponzori</value>
</change>
<change>
<field>campaignname</field>
<value>2323232</value>
</change>
<hide>
<field>targetaudience</field>
</hide>
<readonly>
<field>campaignname</field>
</readonly>
<setclass>
<field>somefield</field>
<fieldclass>someCSSclass</fieldclass>
<labelclass>someOtherCSSclass</labelclass>
</setclass>
<collapse>
<block>sponsor</block>
</collapse>
</actions>
</dependency>
<dependency>
…
</dependency>
</dependencies>
</map>
Notes
$current_module.'_FieldDependency'
. This will be done using the Global Variable module (like the others) <map>
<originmodule>
<originname>CobroPago</originname>
</originmodule>
<dependencies>
<dependency>
<field>amount</field>
<condition>[{"groupid":"",
"columnname":"vtiger_cobropago:amount:amount:CobroPago_Amount:N",
"comparator":"m",
"value":"0",
"columncondition":""}]
</condition>
<actions>
<change>
<field>paymentcategory</field>
<value>Infrastructure</value>
</change>
</actions>
</dependency>
</dependencies>
</map>
<map>
<originmodule> <originname>CobroPago</originname> </originmodule>
<dependencies>
<dependency>
<field>amount</field>
<condition>[{"groupid":"",
"columnname":"vtiger_cobropago:amount:amount:CobroPago_Amount:N",
"comparator":"m",
"value":"0",
"columncondition":""}]
</condition>
<actions>
<change>
<field>paymentcategory</field>
<value>Infrastructure</value>
</change>
<readonly>
<field>paymentcategory</field>
</readonly>
</actions>
</dependency>
</dependencies>
</map>
<map>
<originmodule> <originname>CobroPago</originname> </originmodule>
<dependencies>
<dependency>
<field>amount</field>
<condition>[{"groupid":"",
"columnname":"vtiger_cobropago:amount:amount:CobroPago_Amount:N",
"comparator":"m",
"value":"0",
"columncondition":""}]
</condition>
<actions>
<setoptions>
<field>paymentcategory</field>
<option>Infrastructure</option>
</setoptions>
<deloptions>
<field>paymentcategory</field>
<option>Taxes</option>
<option>Travel</option>
<option>Stock</option>
<option>Sale</option>
</deloptions>
<change>
<field>paymentcategory</field>
<value>Infrastructure</value>
</change>
</actions>
</dependency>
</dependencies>
</map>
<map>
<originmodule> <originname>Accounts</originname> </originmodule>
<dependencies>
<dependency>
<field>employees</field>
<condition>[{"groupid":"1",
"columnname":"vtiger_accounts:employees:employess:Accounts_Employees:N",
"comparator":"l",
"value":"100",
"columncondition":"and"},
{"groupid":"1",
"columnname":"vtiger_accountbillads:bill_country:bill_country:Accounts_Bill_County:V",
"comparator":"e",
"value":"Spain",
"columncondition":"or"},
{"groupid":"2",
"columnname":"vtiger_accounts:employees:employess:Accounts_Employees:N",
"comparator":"g",
"value":"100",
"columncondition":"and"},
{"groupid":"2",
"columnname":"vtiger_accountbillads:bill_country:bill_country:Accounts_Bill_County:V",
"comparator":"e",
"value":"Albania",
"columncondition":""}]
</condition>
<actions>
<hide>
<br>
<field>ownership</field>
</hide>
<collapse>
<block>LBL_ADDRESS_INFORMATION</block>
</collapse>
</actions>
</dependency>
<dependency>
<field>bill_country</field>
<condition>[{"groupid":"1",
"columnname":"vtiger_accounts:employees:employess:Accounts_Employees:N",
"comparator":"l",
"value":"100",
"columncondition":"and"},
{"groupid":"1",
"columnname":"vtiger_accountbillads:bill_country:bill_country:Accounts_Bill_County:V",
"comparator":"e",
"value":"Spain",
"columncondition":"or"},
{"groupid":"2",
"columnname":"vtiger_accounts:employees:employess:Accounts_Employees:N",
"comparator":"g",
"value":"100",
"columncondition":"and"},
{"groupid":"2",
"columnname":"vtiger_accountbillads:bill_country:bill_country:Accounts_Bill_County:V",
"comparator":"e",
"value":"Albania",
"columncondition":""}]
</condition>
<actions>
<hide>
<field>ownership</field>
</hide>
<collapse>
<block>LBL_ADDRESS_INFORMATION</block>
</collapse>
</actions>
</dependency>
</dependencies>
</map>
<map>
<originmodule> <originname>Invoice</originname> </originmodule>
<dependencies>
<dependency>
<field>invoicedate</field>
<actions>
<function>
<field>duedate</field>
<name>fieldDep_AddDays</name>
<parameters>
<parameter>30</parameter>
</parameters>
</function>
</actions>
</dependency>
</dependencies>
</map>
<map>
<originmodule> <originname>Contacts</originname> </originmodule>
<dependencies>
<dependency>
<field>phone</field>
<actions>
<function>
<field>phone</field>
<name>fieldDep_OnlyNumbers</name>
</function>
</actions>
</dependency>
</dependencies>
</map>
<map>
<originmodule>
<originname>Contacts</originname>
</originmodule>
<dependencies>
<dependency>
<field>firstname</field>
<actions>
<function>
<field>description</field>
<name>fieldDep_GetField</name>
<parameters>
<parameter>Accounts.description</parameter>
<parameter>description</parameter>
</parameters>
</function>
</actions>
</dependency>
</dependencies>
</map>
<map>
<originmodule>
<originname>Potentials</originname>
</originmodule>
<dependencies>
<dependency>
<field>related_to</field>
<actions>
<function>
<field>title</field>
<name>fieldDep_GetField</name>
<parameters>
<parameter>Accounts.industry,Accounts.email1</parameter>
<parameter>potentialname,nextstep</parameter>
</parameters>
</function>
</actions>
</dependency>
</dependencies>
</map>
<map>
<originmodule> <originname>Accounts</originname> </originmodule>
<dependencies>
<dependency>
<field>bill_country</field>
<condition>
[
{"groupid":"1",
"columnname":"bill_country",
"comparator":"s",
"value":"A",
"columncondition":""}
]
</condition>
<actions>
<change>
<field>paymentcategory</field>
<value>Infrastructure</value>
</change>
<readonly>
<field>paymentcategory</field>
</readonly>
</actions>
</dependency>
<dependency>
<field>bill_country</field>
<condition>
[
{"groupid":"1",
"columnname":"bill_country",
"comparator":"ns",
"value":"A",
"columncondition":""}
]
</condition>
<actions>
<editable>
<field>paymentcategory</field>
</editable>
</actions>
</dependency>
</dependencies>
</map>
Another Example:
<map>
<originmodule>
<originname>Accounts</originname>
</originmodule>
<dependencies>
<dependency>
<field>accountname</field>
<condition>
[
{"groupid":"1",
"columnname":"vtiger_contacts:lastname:lastname:Contacts_Contact_Lastname:V",
"comparator":"c",
"value":"sel",
"columncondition":"AND"},
{"groupid":"1",
"columnname":"vtiger_contacts:email:email:Contacts_Contact_Email:V",
"comparator":"c",
"value":"gmail",
"columncondition":"OR"},
{"groupid":"2",
"columnname":"vtiger_contacts:firstname:firstname:Contacts_Contact_Firstname:V",
"comparator":"e",
"value":"albana",
"columncondition":""}
]
</condition>
<actions>
<change>
<field>phone</field>
<value>12345</value>
</change>
<change>
<field>email1</field>
<value>a@gmail.com</value>
</change>
<hide>
<field>otherphone</field>
</hide>
<hide>
<field>assigned_user_id</field>
</hide>
<readonly>
<field>fax</field>
</readonly>
<readonly>
<field>notify_owner</field>
</readonly>
<deloptions>
<field>rating</field>
<option>Acquired</option>
<option>Active</option>
<option>Shutdown</option>
</deloptions>
<collapse>
<block>CustomerPortalInformation</block>
</collapse>
</actions>
</dependency>
<dependency>
<field>accountname</field>
<condition>
[
{"groupid":"1",
"columnname":"vtiger_account:accountname:accountname:Accounts_Account_Name:V",
"comparator":"c",
"value":"corebos",
"columncondition":""}
]
</condition>
<actions>
<change>
<field>phone</field>
<value>54321</value>
</change>
<show>
<block>assigned_user_id</block>
</show>
<setoptions>
<field>rating</field>
<option>Market Failed</option>
</setoptions>
<disappear>
<block>AddressInformation</block>
</disappear>
</actions>
</dependency>
</dependencies>
</map>
<map>
<originmodule> <originname>Invoice</originname> </originmodule>
<dependencies>
<dependency>
<field>cf_879</field>
<actions>
<function>
<field>cf_879</field>
<name>changeTaxDetails</name>
<parameters>
</parameters>
</function>
</actions>
</dependency>
</dependencies>
</map>
function changeTaxDetails(change_field, action_field, new_value, old_value) {
if (action_field == 'cf_879') {
if (new_value != 'Guardias') {
document.getElementsByName('tax2_group_percentage')[0].value = 0.00;
} else {
document.getElementsByName('tax2_group_percentage')[0].value = -6.00;
}
calcTotal();
}
}
you can load the JS from a file in the footer with:
BusinessActions::addLink(getTabid("Invoice"), 'FOOTERSCRIPT', 'corebosjshookinvoice', 'modules/Invoice/corebosjshookinvoice.js', '', 0, null, false);
NOTE: The application is constructed to not permit this so we have to use a trick to consciously force this.
<map>
<originmodule>
<originname>CobroPago</originname>
</originmodule>
<dependencies>
<dependency>
<field>cyp_no</field>
<actions>
<function>
<field>cyp_no</field>
<name>doNothing</name>
<parameters>
<parameter>this is a stub to force the next two functions to work. this is a conscious execution of functions on load</parameter>
</parameters>
</function>
<function>
<field>assigned_user_id</field>
<name>fieldDep_AssignUser</name>
<parameters>
<parameter>12</parameter>
</parameters>
</function>
<function>
<field>reports_to_id</field>
<name>fieldDep_AssignUserSelect</name>
<parameters>
<parameter>5</parameter>
</parameters>
</function>
</actions>
</dependency>
</dependencies>
</map>
<map>
<originmodule>
<originname>AssetDetails</originname> {the target module}
</originmodule>
<dependencies>
<dependency>
<field>related_assets</field> {reference field, so filling this field we will trigger the search for the other ones}
<actions>
<function>
<name>fieldDep_GetFieldSearch</name>
<parameters>
<parameter>Assets</parameter> {the module where we take the desired fields}
<parameter>related_assets_sc,account</parameter> {fields we want to take the value from, in this case Assets}
<parameter>id</parameter>
<parameter>new value</parameter> {default parameter; to set new values to the fields}
<parameter>e</parameter> {operator; in this case means: equal}
<parameter>related_ad_servicecontact,account_name</parameter> {fields where we will copy the values,in this case AssetDetils; the fields must be separated by coma in respective order with the above fields from Assets}
</parameters>
</function>
</actions>
</dependency>
<dependency>
<field>related_productcomponent</field>
<actions>
<function>
<name>fieldDep_GetFieldSearch</name>
<parameters>
<parameter>ProductComponent</parameter>
<parameter>maintenance_buyingprice,maintenance_sellingprice</parameter>
<parameter>id</parameter>
<parameter>new value</parameter>
<parameter>e</parameter>
<parameter>vendor_maintenance_purchasevalue,customer_maintenance_salesvalue</parameter>
</parameters>
</function>
</actions>
</dependency>
</dependencies>
</map>
You can add as many dependencies to copy-paste field values from different modules. As you see above we have used two <dependency>
tags in order to take values from Assets when we fill the related_assets
and to take from ProductComponent filling the related_productcomponent
between <field>
tags.
When we set a field to display type 2 or 4, that field will not be present in the edit view of the record because they are fields that will be automatically filled in the background when saved. This is a problem for Field Dependencies because these happen completely in the browser. The change and the evaluation of the conditions are done in the browser when editing, where the read-only value is not present. If we need to add a dependency with a condition based on a read-only value we have to get the value into the browser. To achieve that we have to add an EDITVIEWHTML
business action with this structure:
<input type="hidden" id="cf_1329" name="cf_1329" value="{$FIELDS['cf_1329']}">
This will add the input with the read-only field value to the browser where the field dependency code can read it and evaluate the conditions.
Both the <dependency>
and the <action>
directives accept an optional directive named <mode>
. If given, this directive indicates if the dependency/action will be applied or not depending on the current mode of the record.
Valid mode values are:
This type of map tends to get very complicated, with a lot of different options and conditions. It can also happen that we may have different variations of the map for the same module depending on the mode
.
To help manage this complexity, the field dependency map permits including sections from other records.
The <dependencies>
, the <dependency>
, and the <action>
directives can be imported from another Business map record.
Invoice_FieldDependency Business Map
<map>
<originmodule> <originname>Invoice</originname> </originmodule>
<dependencies>
<dependency>
<loadfrom>InvoiceDependency</loadfrom>
</dependency>
</dependencies>
</map>
InvoiceDependency Business Map
<dependency>
<field>invoicedate</field>
<actions>
<loadfrom>InvoiceActions</loadfrom>
</actions>
</dependency>
InvoiceActions Business Map
<action>
<function>
<field>duedate</field>
<name>fieldDep_AddDays</name>
<parameters>
<parameter>30</parameter>
</parameters>
</function>
</action>
<map>
<originmodule>
<originname>LandlordInfo</originname>
</originmodule>
<dependencies>
<loadfrom>Test</loadfrom>
</dependencies>
</map>
fieldDep_doJS
is a powerful function that basically executes any javascript code inside of the application. The structure of the map is as below, where return '{{$committente}}' + '{{$insp_type}}'
is the javascript code that is going to be executed:
<dependency>
<field>committente</field>
<actions>
<function>
<field>accountname</field>
<name>fieldDep_doJS</name>
<parameters>
<parameter>return '{{$firstname}}' + '{{$lastname}}'</parameter>
</parameters>
</function>
</actions>
</dependency>
{{$fieldname}}
is a field in the application (Detailview/Editview) that you want to get the value ofreturn
is a ''must'' for every code that you have to add it represents the value the Field Dependency will use to update the target field<parameter>you code here without new lines</parameters>
return '{{$firstname}}' + '{{$lastname}}'
return new Date().toJSON().slice(0, 10);
const inputDate = '{{$datarichiestaintegrinsp}}';const parts = inputDate.split('-');const formattedDate = `${parts[2]}-${parts[1]}-${parts[0]}`;const givenDate = new Date(formattedDate);givenDate.setDate(givenDate.getDate() + 30); return givenDate.toISOString().slice(0, 10);
Next | Chapter 14: Validations.