Things to Note
Overriding a core SugarBean class comes with some caveats:
The custom class is only used when the product core code uses theÂ BeanFactory.Â
For most circumstances, Sugar is set up to use BeanFactory, however, legacy code or specific logic that is pairs core modules together may use direct calls to a core SugarBean class, which would then cause the custom class to not be used. In those scenarios,Â it is recommended to use a logic look instead.
Extending the Cases module doesn't affect the email Import process.
If theÂ $objectListÂ property is not defined for the module, the custom object will be used, however, things like Studio will no longer work correctly.
For the example above, we defined the $objectList property for the Leads module to make sure that Studio continued working as expected. Without this definition, if you navigate to Admin > Studio, fields and relationships will not render properly.
  	Last modified: 2023-02-03 21:04:03


Implementing Custom SugarBean Templates
Overview
This article covers how to implement custom SugarBean templates, which can then be extended by custom modules. It also covers vardef templates, which can more portable and used by multiple modules. By default, Sugar comes with a few templates such as Company, Person, Issue, and File templates.
Creating a Custom SugarBean Template
The following example will create a custom Bean template that can be used by custom modules to shrink down the overall size of the Bean model, by only implementing the two fields required by all modules, the id field and the deleted field. In order to do this, we will have to override some core SugarBean methods, as the base SugarBean is statically configured to do things like Save/Delete the model with the date_entered, date_modified, modified_user_id and created_user_id fields. In order to accommodate custom Beans that wish to use those fields, this template will also have properties for configuring those types of fields so that they are populated using the default SugarBean logic.
To create a template for use by custom modules, you create a class that extends from SugarBean inÂ ./custom/include/SugarObjects/templates/<template_name>/<class_name>.php. For this example, we will create the "bare" template with the following file:
./custom/include/SugarObjects/templates/bare/BareBean.php
<?php
class BareBean extends SugarBean
{
    /**
     * The configured modified date field
     * @var string|false
     */
    protected $_modified_date_field = false;
    /**
     * The configured modified user field
     * @var string|false
     */
    protected $_modified_user_field = false;
    /**
     * The configured created date field
     * @var string|false
     */
    protected $_created_date_field = false;
    /**
     * The configured created user field


/**
     * The configured created user field
     * @var string|false
     */
    protected $_created_user_field = false;
    /**
     * Get the field name where modified date is stored, if in use by Module
     * @return string|false
     */
    public function getModifiedDateField()
    {
        if ($this->_modified_date_field !== FALSE){
            $field = $this->_modified_date_field;
            if (isset($this->field_defs[$field]) && $this->field_defs[$field]['type'] == 'datetime'){
                return $field;
            }
        }
        return FALSE;
    }
    /**
     * Override the stock setModifiedDate method
     *  - Check that field is in use by this Module
     *  - If in use, set the configured field to current time
     * @inheritdoc
     **/
    public function setModifiedDate($date = '')
    {
        global $timedate;
        $field = $this->getModifiedDateField();
        if ($field !== FALSE){
            // This code was duplicated from the stock SugarBean::setModifiedDate
            if ($this->update_date_modified || empty($this->$field)) {
                // This only needs to be calculated if it is going to be used
                if (empty($date)) {
                    $date = $timedate->nowDb();
                }
                $this->$field = $date;
            }
        }
    }
    /**
     * Get the field name where modified user ID is stored, if in use by Module
     * @return string|false
     **/
    public function getModifiedUserField()
    {
        if ($this->_modified_user_field !== FALSE){


{
        if ($this->_modified_user_field !== FALSE){
            $field = $this->_modified_user_field;
            if (isset($this->field_defs[$field]) && $this->field_defs[$field]['type'] == 'assigned_user_name'){
                return $field;
            }
        }
        return FALSE;
    }
    /**
     * Override the stock setModifiedUser Method
     * - Check that field is in use by this Module
     * - If in use, set the configured field to user_id
     * @inheritdoc
     * @param User|null $user [description]
     */
    public function setModifiedUser(User $user = null)
    {
        global $current_user;
        $field = $this->getModifiedUserField();
        if ($field !== FALSE){
            // If the update date modified by flag is set then carry out this directive
            if ($this->update_modified_by) {
                // Default the modified user id to the default
                $this->$field = 1;
                // If a user was not presented, default to the current user
                if (empty($user)) {
                    $user = $current_user;
                }
                // If the user is set, use it
                if (!empty($user)) {
                    $this->$field = $user->id;
                }
            }
        }
    }
    /**
     * Get the field name where created date is stored, if in use by Module
     * @return string|false
     */
    public function getCreatedDateField()
    {
        if ($this->_created_date_field !== FALSE){
            $field = $this->_created_date_field;


$field = $this->_created_date_field;
            if (isset($this->field_defs[$field]) && $this->field_defs[$field]['type'] == 'datetime'){
                return $field;
            }
        }
        return FALSE;
    }
    /**
     * Get the field name where created user ID is stored, if in use by Module
     * @return string|false
     */
    public function getCreatedUserField()
    {
        if ($this->_created_user_field !== FALSE){
            $field = $this->_created_user_field;
            if (isset($this->field_defs[$field]) && $this->field_defs[$field]['type'] == 'assigned_user_name'){
                return $field;
            }
        }
        return FALSE;
    }
    /**
     * Override the stock setCreateData method
     * - Code was duplicated from stock, to accommodate not having created date or created user fields
     * @inheritdoc
     */
    public function setCreateData($isUpdate, User $user = null)
    {
        if (!$isUpdate) {
            global $current_user;
            $field = $this->getCreatedDateField();
            if ($field !== FALSE){
                //Duplicated from SugarBean::setCreateData with modifications for dynamic field name
                if (empty($this->$field)) {
                    $this->$field = $this->getDateModified();
                }
                if (empty($this->$field)){
                    global $timedate;
                    $this->$field = $timedate->nowDb();
                }
            }
            $field = $this->getCreatedUserField();
            if ($field !== FALSE){
                //Duplicated from SugarBean::setCreateData with modifications for dynamic field name


//Duplicated from SugarBean::setCreateData with modifications for dynamic field name
                if ($this->set_created_by == true) {
                    // created by should always be this user
                    // unless it was set outside of the bean
                    if ($user) {
                        $this->$field = $user->id;
                    } else {
                        $this->$field = isset($current_user) ? $current_user->id : "";
                    }
                }
            }
            if ($this->new_with_id == false) {
                $this->id = Sugarcrm\Sugarcrm\Util\Uuid::uuid1();
            }
        }
    }
    /**
     * Get the Date Modified fields value
     * @return mixed|false
     */
    public function getDateModified()
    {
        if ($this->_modified_date_field !== FALSE){
            $field = $this->_modified_date_field;
            return $this->$field;
        }
        return FALSE;
    }
    /**
     * Get the Date Created Fields value
     * @return mixed|false
     */
    public function getDateCreated()
    {
        if ($this->_created_date_field !== FALSE){
            $field = $this->_created_date_field;
            return $this->$field;
        }
        return FALSE;
    }
    /**
     * @inheritdoc
     */
    public function mark_deleted($id)
    {
        $date_modified = $GLOBALS['timedate']->nowDb();
        if (isset($_SESSION['show_deleted'])) {
            $this->mark_undeleted($id);
        } else {
            // Ensure that Activity Messages do not occur in the context of a Delete action (e.g. unlink)


// and do so for all nested calls within the Top Level Delete Context
            $opflag = static::enterOperation('delete');
            $aflag = Activity::isEnabled();
            Activity::disable();
            // call the custom business logic
            $custom_logic_arguments['id'] = $id;
            $this->call_custom_logic("before_delete", $custom_logic_arguments);
            $this->deleted = 1;
            if (isset($this->field_defs['team_id'])) {
                if (empty($this->teams)) {
                    $this->load_relationship('teams');
                }
                if (!empty($this->teams)) {
                    $this->teams->removeTeamSetModule();
                }
            }
            $this->mark_relationships_deleted($id);
            $updateFields = array('deleted' => 1);
            $field = $this->getModifiedDateField();
            if ($field !== FALSE){
                $this->setModifiedDate();
                $updateFields[$field] = $this->$field;
            }
            $field = $this->getModifiedUserField();
            if ($field !== FALSE){
                $this->setModifiedUser();
                $updateFields[$field] = $this->$field;
            }
            $this->db->updateParams(
                $this->table_name,
                $this->field_defs,
                $updateFields,
                array('id' => $id)
            );
            if ($this->isFavoritesEnabled()) {
                SugarFavorites::markRecordDeletedInFavorites($id, $date_modified);
            }
            // Take the item off the recently viewed lists
            $tracker = BeanFactory::newBean('Trackers');
            $tracker->makeInvisibleForAll($id);


$tracker->makeInvisibleForAll($id);
            SugarRelationship::resaveRelatedBeans();
            // call the custom business logic
            $this->call_custom_logic("after_delete", $custom_logic_arguments);
            if(static::leaveOperation('delete', $opflag) && $aflag) {
                Activity::enable();
            }
        }
    }
    /**
     * @inheritdoc
     */
    public function mark_undeleted($id)
    {
        // call the custom business logic
        $custom_logic_arguments['id'] = $id;
        $this->call_custom_logic("before_restore", $custom_logic_arguments);
        $this->deleted = 0;
        $modified_date_field = $this->getModifiedDateField();
        if ($modified_date_field !== FALSE){
            $this->setModifiedDate();
        }
        $query = "UPDATE {$this->table_name} SET deleted = ?".(!$modified_date_field?"":",$modified_date_field = ?")." WHERE id = ?";
        $conn = $this->db->getConnection();
        $params = array($this->deleted);
        if ($modified_date_field){
            $params[] = $this->$modified_date_field;
        }
        $params[] = $id;
        $conn->executeQuery($query, $params);
        // call the custom business logic
        $this->call_custom_logic("after_restore", $custom_logic_arguments);
    }
}
With the Bean template in place, we can define the default vardefs for the template by creating the following file:
./custom/include/SugarObjects/templates/bare/vardefs.php
<?php
$vardefs = array(
    'audited' => false,
    'favorites' => false,
    'activity_enabled' => false,


'favorites' => false,
    'activity_enabled' => false,
    'fields' => array(
        'id' => array(
            'name' => 'id',
            'vname' => 'LBL_ID',
            'type' => 'id',
            'required' => true,
            'reportable' => true,
            'duplicate_on_record_copy' => 'no',
            'comment' => 'Unique identifier',
            'mandatory_fetch' => true,
        ),
        'deleted' => array(
            'name' => 'deleted',
            'vname' => 'LBL_DELETED',
            'type' => 'bool',
            'default' => '0',
            'reportable' => false,
            'duplicate_on_record_copy' => 'no',
            'comment' => 'Record deletion indicator'
        )
    ),
    'indices' => array(
        'id' => array(
            'name' => 'idx_' . preg_replace('/[^a-z0-9_\-]/i', '', strtolower($module)) . '_pk',
            'type' => 'primary',
            'fields' => array('id')
        ),
        'deleted' => array(
            'name' => 'idx_' . strtolower($table_name) . '_id_del',
            'type' => 'index',
            'fields' => array('id', 'deleted')
        )
    ),
    'duplicate_check' => array(
        'enabled' => false
    )
);
Note: This vardef template also shrinks the SugarBean template down even further, by defaulting auditing, favorites, and activity stream to false.
Using a Custom Bean Template


Using a Custom Bean Template
With the custom SugarBean template in place, we can now implement the template on a custom module. Typically if you deployed a module from Module Builder, there would be two classes generated by Sugar, "custom_Module_sugar" and "custom_Module". The "_sugar" generated class extends from the Bean template that was selected in Module Builder, and the primary Bean class is what is utilized in the system and where your development would take place. For the above example template that is created, the assumption is that you are writing the Bean class, rather than using the generated classes by Module Builder, or at the very least removing the intermediary "_sugar" class generated by Module Builder and extending from the desired template. If you deployed a module via Module Builder, and are going to switch to a custom template, please note that some of the stock templates contain internal logic for the setup of fields for that template and that would need to be duplicated if you intend to continue using those fields. The stock templates, located inÂ ./include/SugarObjects/templates/, are available for your reference to copy over any of the required logic to your custom Bean class. With all that being said, let us take a look at a custom Bean class that extends from our custom template.
This example will use the "custom_Module" module which is a module that will be used for Tagging records. Since the module is just storing tags, a slimmed down Bean works well as we only need a "name" field to store those tags. The following class would implement the custom Bean template created above.
./modules/custom_Module/custom_Module.php
<?php
require_once 'custom/include/SugarObjects/templates/bare/BareBean.php';
class custom_Module extends BareBean {
    public $new_schema = true;
    public $module_dir = 'custom_Module';
    public $object_name = 'custom_Module';


public $object_name = 'custom_Module';
    public $table_name = 'custom_Module';
    public $importable = true;
    public $id;
    public $name;
    public $deleted;
    public function __construct(){
        parent::__construct();
    }
    public function bean_implements($interface){
        switch($interface){
            case 'ACL': return true;
        }
        return false;
    }
}
With the modules SugarBean class created, the other thing that needs to be implemented is the vardefs.php file:
./modules/custom_Module/vardefs.php
<?php
$module = 'custom_Module';
$table_name = strtolower($module);
$dictionary[$module] = array(
    'table' => $table_name,
    'fields' => array(
        'name' => array(
            'name' => 'name',
            'vname' => 'LBL_NAME',
            'type' => 'username',
            'link' => true,
            'dbType' => 'varchar',
            'len' => 255,
            'unified_search' => true,
            'full_text_search' => array(),
            'required' => true,
            'importable' => 'required',
            'duplicate_merge' => 'enabled',
            //'duplicate_merge_dom_value' => '3',
            'merge_filter' => 'selected',
            'duplicate_on_record_copy' => 'always',
        ),
    ),
    'relationships' => array (
    ),
    'optimistic_locking' => false,
    'unified_search' => false,
);
VardefManager::createVardef(
    $module,
    $module,


VardefManager::createVardef(
    $module,
    $module,
    //Specify the bare template to be used to create the vardefs
    array('bare')
);
With these files implemented, the "custom_Module" module would implement the "bare" template we created.
Creating Custom Vardef Templates
For some customizations, you might not need a SugarBean template as you may not be implementing logic that needs to be shared across multiple module's Bean classes. However, you may have field definitions that are common across multiple modules that would be beneficial for implementing as Vardef Templates. To create a vardef template, a file as follows, ./custom/include/SugarObjects/implements/<template_name>/vardefs.php.
Continuing on with our example custom_Module module from above, we might want to have a creation date on this custom tags module since our 'bare' SugarBean template does not come with one by default. We could easily just add one in the modules vardef file, but for our example purposes, we know that we will use our 'bare' SugarBean template on other customizations, and on some of those we might also want to include a creation date. To implement the vardef template for the creation date field, we create the following:
./custom/include/SugarObjects/implements/date_entered/vardefs.php
<?php
$vardefs = array(
    'fields' => array(
        'date_entered' => array(
            'name' => 'date_entered',
            'vname' => 'LBL_DATE_ENTERED',
            'type' => 'datetime',
            'group' => 'created_by_name',
            'comment' => 'Date record created',
            'enable_range_search' => true,
            'options' => 'date_range_search_dom',
            'studio' => array(


'options' => 'date_range_search_dom',
            'studio' => array(
                'portaleditview' => false,
            ),
            'duplicate_on_record_copy' => 'no',
            'readonly' => true,
            'massupdate' => false,
            'full_text_search' => array(
                'enabled' => true,
                'searchable' => false
            ),
        )
    ),
    'indices' => array(
        'date_entered' => array(
            'name' => 'idx_' . strtolower($table_name) . '_date_entered',
            'type' => 'index',
            'fields' => array('date_entered')
        )
    )
);
Using a Custom Vardef Template
Once the vardef template is in place, you can use the template by adding it to the 'uses' array property of your module vardefs. Continuing with our example module custom_Module, we can update the vardefs file as follows:
./modules/custom_Module/vardefs.php
<?php
$module = 'custom_Module';
$table_name = strtolower($module);
$dictionary[$module] = array(
    'table' => $table_name,
    'fields' => array(
        'name' => array(
            'name' => 'name',
            'vname' => 'LBL_NAME',
            'type' => 'username',
            'link' => true,
            'dbType' => 'varchar',
            'len' => 255,
            'unified_search' => true,
            'full_text_search' => array(),
            'required' => true,
            'importable' => 'required',
            'duplicate_merge' => 'enabled',
            //'duplicate_merge_dom_value' => '3',


//'duplicate_merge_dom_value' => '3',
            'merge_filter' => 'selected',
            'duplicate_on_record_copy' => 'always',
        ),
    ),
    'relationships' => array (
    ),
    //Add the desired vardef templates to this list
    'uses' => array(
        'date_entered'
    ),
    'optimistic_locking' => false,
    'unified_search' => false,
);
VardefManager::createVardef(
    $module,
    $module,
    //Specify the bare template to be used to create the vardefs
    array('bare')
);
After a Quick Repair and Rebuild, a SQL statement should be generated to update the table of the module with the new date_entered field that was added to the vardefs using the vardef template.
  	Last modified: 2023-02-03 21:04:03


BeanFactory
Overview
The BeanFactory class, located in ./data/BeanFactory.php, is used for loading an instance of a SugarBean. This class should be used any time you are creating or retrieving bean objects. It will automatically handle any classes required for the bean.
Creating a SugarBean Object
newBean()
To create a new, empty SugarBean, use the newBean() method. This method is typically used when creating a new record for a module or to call properties of the module's bean object.
$bean = BeanFactory::newBean($module);
newBeanByName()
Used to fetch a bean by its beanList name.
$bean = BeanFactory::newBeanByName($name);
Retrieving a SugarBean Object
getBean()
The getBean() method can be used to retrieve a specific record from the database. If a record id is not passed, a new bean object will be created.
$bean = BeanFactory::getBean($module, $record_id);
Note: Disabling row-level security when accessing a bean should be set to trueÂ only when it is absolutely necessary to bypass security, for example when updating a Lead record from a custom Entry Point.Â An example of accessing the bean while bypassing row security is:
$bean = BeanFactory::getBean($module, $record_id, array('disable_row_level_security' => true));
retrieveBean()
The retrieveBean() method can also be used to retrieve a specific record from the database. The difference between this method and getBean() is that null will be returned instead of an empty bean object if the retrieve fails.
$bean = BeanFactory::retrieveBean($module, $record_id);


$bean = BeanFactory::retrieveBean($module, $record_id);
Note: Disabling row-level security when accessing a bean should be set to true only when it is absolutely necessary to bypass security, for example, when updating a Lead record from a custom Entry Point.Â An example of accessing the bean while bypassing row security is:
$bean = BeanFactory::retrieveBean($module, $record_id, array('disable_row_level_security' => true));
Retrieving Module Keys
getObjectName()
The getObjectName() method will return the object name / dictionary key for a given module. This is normally the same as the bean name, but may not be for some modules such as Cases which has a key of 'aCase' and a name of 'Case'.
$moduleKey = BeanFactory::getObjectName($moduleName);
getBeanName()
The getBeanName() method will retrieve the bean class name given a module name.
$moduleClass = BeanFactory::getBeanName($module);
Â
  	Last modified: 2023-02-03 21:04:03


Vardefs
Overview
Vardefs (Variable Definitions) provide the Sugar application with information about SugarBeans. Vardefs specify information on the individual fields, relationships between beans, and the indexes for a given bean.Â
Each module that contains a SugarBean file has a vardefs.php file located in it, which specifies the fields for that SugarBean. For example, the vardefs for the Contact bean are located in ./modules/Contacts/vardefs.php.
Dictionary Array
Vardef files create an array called $dictionary, which contains several entries including tables, fields, indices, and relationships.
table : The name of the database table (usually, the name of the module) for this bean that contains the fields
audited : Specifies whether the module has field-level auditing enabled
duplicate_checkÂ : Determines if duplicate checking is enabled on the module, and what duplicate check strategy will be used if enabled.Â
fields : A list of fields and their attributes
indices : A list of indexes that should be created in the database
optimistic_locking : Determines whether the module has optimistic locking enabled
Optimistic locking prevents loss of data by using the bean's modified date to ensure that it is not being modified simultaneously by more than one person or process.
unified_search : Determines whether the module canÂ be searched via Global SearchÂ This setting defaults to false and has no effect if all of the fields in the fields array have the 'unified_search' field attribute set to false.
unified_search_default_enabled : Determines whether the module should be searched by default for new users via Global Search
This setting defaults to true but has no effect ifÂ unified_search is set to false.
visibilityÂ : A list of visibility classes enabled on the module
Duplicate Check Array


visibilityÂ : A list of visibility classes enabled on the module
Duplicate Check Array
The duplicate_checkÂ array contains two properties, that control if duplicate checking is enabled on the module, and which duplicate check strategy will be used to check for duplicates.
The two properties for the array are as follows:
Name
Type
Description
enabled
Boolean
Specifies whether or not the Bean is utilizing the duplicate check framework
<class_name>
Array
<class_name> is the name of the duplicate check strategy class that is handling the duplicate checking. It is set to an array of Metadata, specific to the strategy defined inÂ the key. Review the Duplicate Check Framework for further information.
Fields Array
The fields array contains one array for each field in the SugarBean. At the top level of this array, the key is the name of the field, and the value is an array of attributes about that field.
The list of possible attributes are as follows:
name : The name of the field
vname : The language pack ID for the label of this field
type : The type of the attribute
assigned_user_name : A linked user name
bool : A boolean value
char : A character array
date : A date value with no time
datetime : A date and time
email : An email address field
enum : An enumeration (dropdown list from the language pack)
id : A database GUID
image : A photo-type field
link : A link through an explicit relationship. This attribute should only be used when working with related fields. It does not make the field render as a link.
name : A non-database field type that concatenates other field values
phone : A phone number field to utilize with callto:// links
relate : Related bean
team_list : A team-based ID
text : A text area field
url : A hyperlinked field on the detail view
varchar : A variable-sized string


url : A hyperlinked field on the detail view
varchar : A variable-sized string
table : The database table the field comes from.
The table attribute is only neededÂ to join fields from another table outside of the module in focus.
isnull : Whether the field can contain a null value
len : The length of the field (number of characters if a string)
options : The name of the enumeration (dropdown list) in the language pack for the field
dbtype : The database type of the field (if different than the type)
reportable : Determines whether the field will be available in the Reports and Workflow modules
default :Â The default value for this field.Â Default values for the record are populated by default on create for the record view (for Sidecar modules) and edit view (for Legacy modules) layout but can be modified by users. The Default Value option is available for all data type fields except HTML, Image, Flex Relate, and Relate.
massupdate : Determines whether the fieldÂ will show up in the mass-update panel on its module's list view
Some field types are restricted from mass update regardless of this setting.
rname : For relate-type fields, the field from the related variable that containsÂ the text
id_name : For relate-type fields, the field from the bean that stores the ID for the related bean
source : Set to 'non-db' if the field value does not come from the database
The source attribute can be used for calculated values or values retrieved in some other way.
sort_on : The field to sort by if multiple fields are used in the presentation of field's information
fields : For concatenated values, an array containing the fields that are concatenated
db_concat_fields : For concatenated values, an array containing the fields to concatenate in the database
unified_search : Determines whether the fieldÂ canÂ be searched via Global Search


unified_search : Determines whether the fieldÂ canÂ be searched via Global Search
This has no effect if the dictionary array setting 'unified_search' is not set to true.
enable_range_search : For date, datetime, and numeric fields, determines if this field should be searchable by range in module searches
dependency : Allows a field to have a predefined formula to control the field's visibility
studio : Controls the visibility of the field in the Studio editor
If set to false, then the field will not appear in any studio screens for the module. Otherwise, you may specify an Array of view keys from which the field's visibility should be removed (e.g.Â array('listview'=>false) will hide the field in the listview layout screen).
The following example illustrates a standard ID field for a bean:
'id' => array (
    'name' => 'id',
    'vname' => 'LBL_ID',
    'type' => 'id',
    'required' => true,
),
Indices Array
This array contains a list of arrays that are used to create indices in the database. The fields in this array are:
name : The unique name of the index
type : The type of index (primary, unique, or index)
fields : An ordered array of the fields to index
source : Set to 'non-db' if you are creating an index for added application functionality such as duplication checking on imports
The following exampleÂ creates a primary index called 'userspk' on the 'id' column:
array(
    'name' => 'userspk',
    'type' => 'primary',
    'fields'=> array('id')
),
Relationships Array
The relationships array specifies relationships between beans. Like the indices array entries, it is a list of names with array values.


lhs_module : The module on the left-hand side of the relationship
lhs_table : The table on the left-hand side of the relationship
lhs_key : The primary key column of the left-hand side of the relationship
rhs_module : The module on the right-hand side of the relationship
rhs_table : The table on the right-hand side of the relationship
rhs_key : The primary key column of the right-hand side of the relationship
relationship_type : The type of relationship (e.g. one-to-many,Â many-to-many)
relationship_role_column : The type of relationship role
relationship_role_column_value : Defines the unique identifier for the relationship role
The following example creates a relationship between a contact and the contact that they report to. The reports_to_id field maps to the id of the record that belongs to the higher-ranked contact. This is a one-to-many relationship in that a contact may only report to one person, but many people may report to the same contact.
'contact_direct_reports' => array(
    'lhs_module' => 'Contacts',
    'lhs_table' => 'contacts',
    'lhs_key' => 'id',
    'rhs_module' => 'Contacts',
    'rhs_table' => 'contacts',
    'rhs_key' => 'reports_to_id',
    'relationship_type' => 'one-to-many'
),
Visibility Array
The visibilityÂ array specifies the row level visibility classes that are enabled on the bean. Each entry in the array, is a key-value pair, where the key is the name of the Visibility class and the value is set to boolean True. More information on configuring custom Visibility strategies can be found in the Architecture section under Visibility Framework.
Extending Vardefs
More information about extending and overriding vardefs can be found in the Extensions Framework documentation under Vardefs.


TopicsManually Creating Custom FieldsThe most common way to create custom fields in Sugar is via Studio inside the application. This page describes how to use the ModuleInstaller class or vardef extensions as alternative methods of creating custom fields.Specifying Custom Indexes for Import Duplicate CheckingWhen importing records to Sugar via the Import Wizard, users can select which of the mapped fields they would like to use to perform a duplicate check and thereby avoid creating duplicate records. This article explains how to enable an additional field or set of fields for selection in this step.Working With IndexesSugar provides a simple method for creating custom indexes through the vardef framework. Indexes can be built on one or more fields within a module. Indexes can be saved down to the database level or made available only in the application for functions such as Import Duplicate Checking.
  	Last modified: 2023-02-03 21:04:03


Working With Indexes
Overview
Sugar provides a simple method for creating custom indexes through the vardef framework. Indexes can be built on one or more fields within a module. Indexes can be saved down to the database level or made available only in the application for functions such as Import Duplicate Checking.
Index Metadata
Indexes have the followingÂ metadata options that can be configured per index:
Key
Value
DescriptionÂ
name
string
A Unique identifier to define the index. Best practices recommend indexes start with idx and contain the suffix cstm to avoid conflicting with a stock index.Â
Note : Some databases have restrictions on the length of index names. Please check with your database vendor to avoid any issues.
type
stringÂ
Â All indexes should use the type of "index"
fields
array
A PHP array of the fields for the index to utilizeÂ
source
string
Specify as "non-db" to avoid creating the index in the database
Creating Indexes
Stock indexes are initially defined in the module's vardefs file under the indices array. For reference, you can find them using the vardef pathÂ of your module. The path will be Â ./modules/<module>/vardefs.php.
Custom indexesÂ should Â be created using the Extension Framework.Â First, create a PHPÂ file in the extension directory of yourÂ desired module. The path should similar toÂ ./custom/Extension/modules/<module>/Ext/Vardefs/<name>.php.
In the new file, add the appropriate $dictionary reference to define the custom index:
<?php
$dictionary['<module>']['indices'][] = array(
	'name' => '<index name>',
	'type' => 'index',
	'fields' => array(
		'field1',
		'field2',
	)
);


'field1',
		'field2',
	)
);
Note : For performance reasons, it is not recommended to create an index on a single field unless the source is set to non-db.
Once installed,you will need to navigateÂ to Admin > Repair > Quick Repair and Rebuild to enable the custom index. You will need to execute any scripts generated by the rebuild process.
Removing Indexes
Stock indexes are initially defined in the module's vardefs file under the indices array. For reference, you can find them using the vardef path of your module. The path will be ./modules/<module>/vardefs.php.
Stock indexes should be removed using the Extension Framework. First, create a PHP file in the extension directory of your desired module. The path should similar to ./custom/Extension/modules/<module>/Ext/Vardefs/<name>.php.
In the new file, loop through the existing 'indices' sub-array of the $dictionary to locate the stock index to remove, and use unset() to remove it from the array.
Example
The following is an example to remove the idx_calls_date_start index from the Call module's vardefs.
First, create ./custom/Extension/modules/Calls/Ext/Vardefs/remove_idx_calls_date_start.phpÂ from the root directory of your Sugar installation on the web server. When creating the file, keep in mind the following requirements:
The name of the file is not important, as long as it ends with a .php extension.
The rest of the directory path is case sensitive so be sure to create the directories as shown.
If you are removing the index for a module other than Calls, then substitute the corresponding directory name with that module.
Ensure that the entire directory path and file have the correct ownership and sufficient permissions for the web server to access the file.
The contents of the file should look similar to the following code:


The contents of the file should look similar to the following code:
<?php
$call_indexes = $dictionary['Call']['indices'];
$remove_index = "idx_calls_date_start";
foreach($call_indexes as $index_key => $index_item) {
    if( $index_item['name'] == $remove_index ) {
        unset($dictionary['Call']['indices'][$index_key]);
    }
}
Note : Removing the reference to the index from the module's indices array does not actually remove the index from the module's database table. Removing the reference from the indices array ensures that the index is not added back to the module's database table when performing any future Quick Repair and Rebuilds. The database index must be removed directly at the database level. On MySQL, with the current example, this could be done with a query like:
ALTER TABLE calls DROP INDEX idx_calls_date_start;
Once installed,you will need to navigate to Admin > Repair > Quick Repair and Rebuild to remove the index from the $dictionary array. You will need to execute any scripts generated by the rebuilding process.
Creating Indexes for Import Duplicate Checking
When importing records to Sugar via the Import Wizard, users can select which of the mapped fields theyÂ would like to use to perform a duplicate checkÂ and thereby avoid creating duplicate records. The following instructionsÂ explain how to enable an additional field or set of fields for selection in this step.
Example
The following is an example to add the home phone field to the Contact module's duplicate check.
First, create ./custom/Extension/modules/Contacts/Ext/Vardefs/custom_import_index.phpÂ from the root directory of your Sugar installation on theÂ web server. When creating the file, keep in mind the following requirements:
The name of the file is not important, as long as it ends with a .php extension.


The rest of the directory path is case sensitive so be sure to create the directories as shown.
If you are creating the import index for a module other than Contacts, then substitute the corresponding directory name with that module.
Ensure that the entire directory path and file have the correct ownership and sufficient permissions for the web server to access the file.
The contents of the file should look similar to the following code:
<?php
$dictionary['Contact']['indices'][] = array(
     'name' => 'idx_home_phone_cstm',
     'type' => 'index',
     'fields' => array(
         0 => 'phone_home',
     ),
     'source' => 'non-db',
);
PleaseÂ note that the module name in line 2 of the code is singular (i.e. Contact, not Contacts). If you are unsure of what to enter for the module name, you can verify the name by opening the ./cache/modules/<module_name>/<module_name>vardefs.php file. The second line of that file will have text like the following:
$GLOBALS["dictionary"]["Contact"] = array (...);
The parameter following "dictionary" is the same parameter you should use in the file defining theÂ custom index. To verify duplicates against a combination of fields (i.e. duplicates will only be flagged if the values of multiple fields match those of an existing record), then simplyÂ add the desired fields to the 'fields' array in the code example.
Finally, navigateÂ to Admin > Repair > Quick Repair and Rebuild to enable the custom index for duplicate verification when importing records in theÂ module.Â
  	Last modified: 2023-02-03 21:04:03


Manually Creating Custom Fields
Overview
The most common way to create custom fields in Sugar is via Studio inside the application. This page describes how to use the ModuleInstaller classÂ or vardef extensions as alternative methods ofÂ creating custom fields.
Note: Sugar Sell Essentials customers do not have the ability to upload custom file packages to Sugar using Module Loader.
Using ModuleInstaller to Create Custom Fields
There are two ways to create a field using the ModuleInstaller class: via installer package orÂ programmatically. An example of creating a field from a module-loadable package is explained in the Module Loader documentation,,Â Creating an Installable Package that Creates New Fields. The following example shows how toÂ programmatically add custom fields using the ModuleInstaller class with the install_custom_fields() method:
<?php
    $fields = array (
        //Text
        array(
            'name' => 'text_field_example',
            'label' => 'LBL_TEXT_FIELD_EXAMPLE',
            'type' => 'varchar',
            'module' => 'Accounts',
            'help' => 'Text Field Help Text',
            'comment' => 'Text Field Comment Text',
            'default_value' => '',
            'max_size' => 255,
            'required' => false, // true or false
            'reportable' => true, // true or false
            'audited' => false, // true or false
            'importable' => 'true', // 'true', 'false', 'required'
            'duplicate_merge' => false, // true or false
        ),
        //DropDown
        array(
            'name' => 'dropdown_field_example',
            'label' => 'LBL_DROPDOWN_FIELD_EXAMPLE',
            'type' => 'enum',
            'module' => 'Accounts',


'type' => 'enum',
            'module' => 'Accounts',
            'help' => 'Enum Field Help Text',
            'comment' => 'Enum Field Comment Text',
            'ext1' => 'account_type_dom', //maps to options - specify list name
            'default_value' => 'Analyst', //key of entry in specified list
            'mass_update' => false, // true or false
            'required' => false, // true or false
            'reportable' => true, // true or false
            'audited' => false, // true or false
            'importable' => 'true', // 'true', 'false' or 'required'
            'duplicate_merge' => false, // true or false
        ),
        //MultiSelect
        array(
            'name' => 'multiselect_field_example',
            'label' => 'LBL_MULTISELECT_FIELD_EXAMPLE',
            'type' => 'multienum',
            'module' => 'Accounts',
            'help' => 'Multi-Enum Field Help Text',
            'comment' => 'Multi-Enum Field Comment Text',
            'ext1' => 'account_type_dom', //maps to options - specify list name
            'default_value' => 'Analyst', //key of entry in specified list
            'mass_update' => false, // true or false
            'required' => false, // true or false
            'reportable' => true, // true or false
            'audited' => false, // true or false
            'importable' => 'true', // 'true', 'false' or 'required'
            'duplicate_merge' => false, // true or false
        ),
        //Checkbox
        array(
            'name' => 'checkbox_field_example',


//Checkbox
        array(
            'name' => 'checkbox_field_example',
            'label' => 'LBL_CHECKBOX_FIELD_EXAMPLE',
            'type' => 'bool',
            'module' => 'Accounts',
            'default_value' => true, // true or false
            'help' => 'Bool Field Help Text',
            'comment' => 'Bool Field Comment',
            'audited' => false, // true or false
            'mass_update' => false, // true or false
            'duplicate_merge' => false, // true or false
            'reportable' => true, // true or false
            'importable' => 'true', // 'true', 'false' or 'required'
        ),
        //Date
        array(
            'name' => 'date_field_example',
            'label' => 'LBL_DATE_FIELD_EXAMPLE',
            'type' => 'date',
            'module' => 'Accounts',
            'default_value' => '',
            'help' => 'Date Field Help Text',
            'comment' => 'Date Field Comment',
            'mass_update' => false, // true or false
            'required' => false, // true or false
            'reportable' => true, // true or false
            'audited' => false, // true or false
            'duplicate_merge' => false, // true or false
            'importable' => 'true', // 'true', 'false' or 'required'
        ),
        //DateTime
        array(
            'name' => 'datetime_field_example',
            'label' => 'LBL_DATETIME_FIELD_EXAMPLE',
            'type' => 'datetime',
            'module' => 'Accounts',
            'default_value' => '',


'module' => 'Accounts',
            'default_value' => '',
            'help' => 'DateTime Field Help Text',
            'comment' => 'DateTime Field Comment',
            'mass_update' => false, // true or false
            'enable_range_search' => false, // true or false
            'required' => false, // true or false
            'reportable' => true, // true or false
            'audited' => false, // true or false
            'duplicate_merge' => false, // true or false
            'importable' => 'true', // 'true', 'false' or 'required'
        ),
        //Encrypt
        array(
            'name' => 'encrypt_field_example',
            'label' => 'LBL_ENCRYPT_FIELD_EXAMPLE',
            'type' => 'encrypt',
            'module' => 'Accounts',
            'default_value' => '',
            'help' => 'Encrypt Field Help Text',
            'comment' => 'Encrypt Field Comment',
            'reportable' => true, // true or false
            'audited' => false, // true or false
            'duplicate_merge' => false, // true or false
            'importable' => 'true', // 'true', 'false' or 'required'
        ),
    );
    require_once('ModuleInstall/ModuleInstaller.php');
    $moduleInstaller = new ModuleInstaller();
    $moduleInstaller->install_custom_fields($fields);
AddÂ labels for custom fields by creating a corresponding language extension file:
./custom/Extension/modules/Accounts/Ext/Language/en_us.<name>.php
<?php
    $mod_strings['LBL_TEXT_FIELD_EXAMPLE'] = 'Text Field Example';
    $mod_strings['LBL_DROPDOWN_FIELD_EXAMPLE'] = 'DropDown Field Example';


$mod_strings['LBL_DROPDOWN_FIELD_EXAMPLE'] = 'DropDown Field Example';
    $mod_strings['LBL_CHECKBOX_FIELD_EXAMPLE'] = 'Checkbox Field Example';
    $mod_strings['LBL_MULTISELECT_FIELD_EXAMPLE'] = 'Multi-Select Field Example';
    $mod_strings['LBL_DATE_FIELD_EXAMPLE'] = 'Date Field Example';
    $mod_strings['LBL_DATETIME_FIELD_EXAMPLE'] = 'DateTime Field Example';
    $mod_strings['LBL_ENCRYPT_FIELD_EXAMPLE'] = 'Encrypt Field Example';
Finally, navigate to Admin > Repair > Quick Repair and Rebuild to make the new field available for users.
Using the Vardef Extensions
You should try to avoid creating your own custom fields using the vardefs as there are several caveats:
If your installation does not already contain custom fields, you must manually create the custom table. Otherwise, the system will not recognize your field's custom vardef. This situation is outlined in the following section.
You mustÂ run a Quick Repair and Rebuild and then execute the generated SQL after the vardef is installed.
You must correctly define the properties of a vardef. If you miss any, the field may not work properly.
Your field name must end with "_c" and have the property 'source' set to 'custom_fields'. This is required as you should not modify core tables in Sugar and it is not permitted onÂ Sugar's cloud service.
Your vardef must specify the exact indexes of the properties you want to set. For example, use: Â $dictionary['<module singular>']['fields']['example_c']['name'] = 'myfield_c';Â instead of $dictionary['<module singular>']['fields']['example_c'] = array(['name' => 'myfield_c');. This will help prevent the system from losing any properties when loading from the extension framework.


The initial challenge when creating your own custom vardef is getting the system to recognize the vardef and generate the database field. ThisÂ issueÂ is illustrated with the example below:
./custom/Extension/modules/<module>/Ext/Vardefs/<file>.php
<?php
$dictionary['<module singular>']['fields']['example_c']['name'] = 'example_c';
$dictionary['<module singular>']['fields']['example_c']['vname'] = 'LBL_EXAMPLE_C';
$dictionary['<module singular>']['fields']['example_c']['type'] = 'varchar';
$dictionary['<module singular>']['fields']['example_c']['enforced'] = '';
$dictionary['<module singular>']['fields']['example_c']['dependency'] = '';
$dictionary['<module singular>']['fields']['example_c']['required'] = false;
$dictionary['<module singular>']['fields']['example_c']['massupdate'] = '0';
$dictionary['<module singular>']['fields']['example_c']['default'] = '';
$dictionary['<module singular>']['fields']['example_c']['no_default'] = false;
$dictionary['<module singular>']['fields']['example_c']['comments'] = 'Example Varchar Vardef';
$dictionary['<module singular>']['fields']['example_c']['help'] = '';
$dictionary['<module singular>']['fields']['example_c']['importable'] = 'true';
$dictionary['<module singular>']['fields']['example_c']['duplicate_merge'] = 'disabled';
$dictionary['<module singular>']['fields']['example_c']['duplicate_merge_dom_value'] = '0';
$dictionary['<module singular>']['fields']['example_c']['audited'] = false;
$dictionary['<module singular>']['fields']['example_c']['reportable'] = true;


$dictionary['<module singular>']['fields']['example_c']['reportable'] = true;
$dictionary['<module singular>']['fields']['example_c']['unified_search'] = false;
$dictionary['<module singular>']['fields']['example_c']['merge_filter'] = 'disabled';
$dictionary['<module singular>']['fields']['example_c']['calculated'] = false;
$dictionary['<module singular>']['fields']['example_c']['len'] = '255';
$dictionary['<module singular>']['fields']['example_c']['size'] = '20';
$dictionary['<module singular>']['fields']['example_c']['id'] = 'example_c';
$dictionary['<module singular>']['fields']['example_c']['custom_module'] = '';
//required to create the field in the _cstm table
$dictionary['<module singular>']['fields']['example_c']['source'] = 'custom_fields';
Once the vardef is in place, determine whether the custom field's module already contains any other custom fields. If there are not any existing custom fields, create a corresponding record in fields_meta_data that will trigger the comparison process.
INSERT INTO fields_meta_data (id, name, vname, comments, custom_module, type, len, required, deleted, audited, massupdate, duplicate_merge, reportable, importable) VALUES ('<module>example_c', 'example_c', 'LBL_EXAMPLE_C', 'Example Varchar Vardef', '<module>', 'varchar', 255, 0, 0, 0, 0, 0, 1, 'true');


Finally, navigate to Admin > Repair > Quick Repair and Rebuild. The system will then rebuild the extensions. After the repair, you will notice a section at the bottom stating that there are differences between the database and vardefs. Execute the scripts generated to create theSaveÂ custom field:
Missing <module>_cstm Table:
/*Checking Custom Fields for module : <module>*/
CREATE TABLE <module>_cstm (id_c char(36)  NOT NULL  , PRIMARY KEY (id_c)) CHARACTER SET utf8 COLLATE utf8_general_ci;
Missing Columns:
/*MISSING IN DATABASE - example_c -  ROW*/
ALTER TABLE <module>_cstm add COLUMN example_c varchar(255)  NULL ;
Â
  	Last modified: 2023-02-03 21:04:03


Specifying Custom Indexes for Import Duplicate Checking
Overview
When importing records to Sugar via the Import Wizard, users can select which of the mapped fields theyÂ would like to use to perform a duplicate checkÂ and thereby avoid creating duplicate records. This article explains how to enable an additional field or set of fields for selection in this step.
Resolution
The import wizard's duplicate check operates based on indices defined for that module. You can create a non-database index to check for a field. It is important that it is non-database as single column indices on your database can hamper overall performance. The following is an example to add the home phone field to the Contact module's duplicate check.
First, create the following file from the root directory of your Sugar installation on theÂ web server:
./custom/Extension/modules/Contacts/Ext/Vardefs/custom_import_index.php
When creating the file, keep in mind the following requirements:
The name of the file is not important, as long as it ends with a .php extension.
The rest of the directory path is case sensitive so be sure to create the directories as shown.
If you are creating the import index for a module other than Contacts, then substitute the corresponding directory name with that module.
Ensure that the entire directory path and file have the correct ownership and sufficient permissions for the web server to access the file.
The contents of the file should look similar to the following code:
<?php
$dictionary['Contact']['indices'][] = array(
     'name' => 'idx_home_phone_cstm',
     'type' => 'index',
     'fields' => array(
         0 => 'phone_home',
     ),
     'source' => 'non-db',
);


),
     'source' => 'non-db',
);
PleaseÂ note that the module name in line 2 of the code is singular (i.e. Contact, not Contacts). If you are unsure of what to enter for the module name, you can verify the name by opening the ./cache/modules/<module_name>/<module_name>vardefs.php file. The second line of that file will have text like the following:
$GLOBALS["dictionary"]["Contact"] = array (
The parameter following "dictionary" is the same parameter you should use in the file defining theÂ custom index. To verify duplicates against a combination of fields (i.e. duplicates will only be flagged if the values of multiple fields match those of an existing record), then simplyÂ add the desired fields to the 'fields' array in the code example.
Finally, navigateÂ to Admin > Repair > Quick Repair and Rebuild to enable the custom index for duplicate verification when importing records in theÂ module.
  	Last modified: 2023-02-03 21:04:03


Fields
Overview
How fields interact with the various aspects of Sugar.Â
SugarField Widgets
The SugarField widgets, located in ./include/SugarFields/Fields/Â ,Â defineÂ the dataÂ formatting and search query structure for the various field types. They also define the rendering of fields for modules running in backward compatibility mode. When creating or overridingÂ field widgets, developers should place their customization in ./custom/include/SugarFields/Fields/. For information on how Sidecar renders fields, please refer to the fields section in our user interfaceÂ documentation.Â Creating Custom Fields
Implementation
All fields for a module are defined withinÂ vardefs. Within this definition, theÂ type attribute will determine all of the logic applied to the field. For example, the Contacts moduleÂ has aÂ  'Do Not Call' field. In the vardefs, thisÂ field is defined as follows:
'do_not_call' => array (
    'name' => 'do_not_call', // the name of the field
    'vname' => 'LBL_DO_NOT_CALL', // the label for the field name
    'type' => 'bool', // the fields type
    'default' => '0', // the fields default value
    'audited'=>true, // whether the field is audited
    'duplicate_on_record_copy' => 'always', // whether to duplicate the fields value when being copied
    'comment' => 'An indicator of whether contact can be called' // admin context of the field
),


),
Â The boolÂ type field is rendered in the UI from the ./clients/base/fields/bool/bool.js field controller which rendersÂ the appropriate handlebars template as defined by the users current view for sidecar enabled modules. When the user saves data, the controller formats the data for the API and passes it to an endpoint. Once the data is received by theÂ server, The SugarFieldÂ definition calls any additional logic in the apiSave function to format the data for saving to the database.Â The same concept is applied in the apiFormatField function when retrieving data from the database to be passed back to the user interface throughÂ the API. For modules running in backward compatibility mode, the bool field is rendered using the Smarty .tpl) templates located in ./include/SugarFields/Fields/Bool/.
While the vardefsÂ define the default type for a field, this value can be overridden in the metadata of the view rendering the field. The example being that in ./custom/modules/Contacts/clients/base/views/record/record.php, you can modify theÂ do_not_call field array to point to a custom field type you have created. For more information on creating custom field types, please refer to Creating Custom Fields documentation.Â Â
Â
  	Last modified: 2023-02-03 21:04:03


Introduction
Overview
The Sugar Developer Guide is an essential resource for developers who are new to Sugar or to CRM and web-based applications. ItÂ describes how to configure and customize the Sugar platform for a broad range of tasks applicable to any organization that has a need to manage business relationships with people.
Prerequisites
Using and understanding theÂ documentation contained in the Sugar Developer Guide requires basic programming and software development knowledge. Specifically, you should be familiar with the PHP general-purpose scripting language and the SQL programming language for accessing databases.
Understanding Sugar's Framework
Designed as the most modern web-based CRM platform available today, Sugar has quickly become the business application standard for companies around the world.Â The Sugar application framework has a sophisticated extension model built into it, allowing developers to make significant customizations to the application in an upgrade-safe and modular manner. It is easy to modify the core files in the distribution; you should always check for an upgrade-safe way to make changes. Educating developers on how to make upgrade-safe customizations is one of the key goals of this Developer Guide. For more information on Sugar's structure, please review the architecture section.
Supported Platforms
Originally, SugarÂ® was written on the LAMP stack (i.e. Linux, Apache, MySQL, and PHP), but has sinceÂ added support for every operating system on which the PHP programming language runs, for the Microsoft IIS web server, and for the Microsoft SQL Server, IBMÂ® DB2Â®, and Oracle databases.Â For more information aboutÂ supported software versions and recommended stacks, please refer to the mainÂ Supported Platforms page.
Sugar Products


Sugar Products
Sugar has several CRM products available: Sugar Sell, Sugar Serve, and Sugar Enterprise, which are all sold under a commercial subscription agreement. TheseÂ products are developed by the same development team using the same source tree with different modules and features available depending on the product. A comparison of each product's features is available in the License Types section of the User Management documentation in the Administration Guide.
Basic Development Rules for Sugar Products
Unless SugarCRM has given you express permission to do so, the following are what not to do when you are configuring, customizing or modifying this Sugar product:
Do not remove or alter any SugarCRM or Sugar copyright, trademark or proprietary notices that appear in the Sugar products.
Do not "fork" the Sugar software (e.g., take a copy of source code from this product and start independent development on it, creating a distinct and separate piece of software).
Do not modify, remove or disable any portion of SugarCRM's "Critical Control Software."
Do not combine or use the Sugar products with any code that is licensed under a prohibited license (e.g., AGPL, GPL v3, Creative Commons or another similar license that would "taint" the Sugar products and require you to share the source code for this product with a third party).
Do not use any part of the Sugar products for the purpose of building a competitive product or service or copying its features or user interface.
Development Tools
Sugar has a set of built-in tools that you can use to your advantage when troubleshooting or developing.
Developer Mode
Developer Mode will allow for Sugar to recompile cached files when the page is reloaded. The following file types are rebuilt:
Handlebar Templates (.hbt)
Smarty Templates (.tpl)
JavaScript ControllersÂ (.js)


Smarty Templates (.tpl)
JavaScript ControllersÂ (.js)
When Developer Mode is enabled, The SidecarÂ JavaScript library references the full JavaScriptÂ files located in ./sidecar/ rather than the concatenated andÂ minified cached versions. You can turn on Developer Mode by navigating to Admin > System Settings. For more information, please refer to the System documentation.
Note: This setting should remain off unless developing because it will degrade system performance.
Diagnostic Tool
When troubleshooting issues, you may find the diagnostic tool to be helpful. This tool will export a zipped package containing the requested diagnostics and is available even if you are hosting your instance on Sugar's cloud service.
The diagnostic tool has the ability to export the following:
SugarCRM config.php
SugarCRM Custom directory
phpinfo()
MySQL - Configuration Table Dumps
MySQL - All Tables Schema
MySQL - General Information
MD5 info
Copy files.md5
Copy MD5 Calculated array
BeanList/BeanFiles files exist
SugarCRM Log File
Sugar schema output (VARDEFS)
You can use the diagnostic tool by navigating to Admin > Diagnostic Tool.Â For more information, please refer to the System documentationÂ in the Administration Guide.
Composer
When building applications, some developers prefer to use Composer to manage their external dependenciesÂ and make them more intuitive. For more information, please refer to the Composer documentation.
Â
TopicsDevelopment MethodologyAn overview of code development methodologies for Sugar platform customizationsComposerUsing Composer as a dependency management system for third party libraries.Delivery and Deployment Guide for EnterprisesGuide to customization deployment steps used by Sugar Professional Services when engaged in CRM projects.Sugar 12.3 to 13.0 Migration GuideThe purpose of this document is to provide insight to Sugar Developers for upgrading custom Sugar code, extensions, and integrations to the Sugar 13.0 (Q2 2023) release.


Last modified: 2023-05-23 16:27:13


Composer
Overview
Composer dependency managementÂ is the de facto standard for managing PHP dependencies. Sugar platform ships with all required code bundled and optimied together for our developers. The installer will, however,Â deploy both composer.json and composer.lock in the web root directory as a reference of all dependencies which are controlled by Composer.
Autoloader
Sugar has a custom autoloader that isÂ PSR-0 and PSR-4Â compliant and integrates with Composer's mapping definitions.
Integrations
When updating the Composer configuration, Composer will generate different mappings based on the settings of every dependency:
Class map
PSR-0 map
PSR-4 map
Include paths (deprecated)
SugarCRM's autoloader uses those generated maps directly to initialize itself and to figure out how to load different classes.
Internals
To prevent endless file stats, Sugar's autoloader maintains a full list of all files at its disposal. This list is only generated once and is maintained in ./cache/file_map.php. Most of the SugarÂ codebase uses the autoloader to determine whether a file is available rather than performing expensive file_exists calls.
A second file,Â storedÂ inÂ ./cache/class_map.phpÂ , maintains a flat list of class-name-to-file mappings. When you make any changes to the file system, clear both files before testing new code.
Optimization
When updating dependencies through Composer, Sugar team uses the optimize flag: composer update --optimize-autoloader --no-dev". Â By doing so, Composer will scan all files in the packages it manages and create a full list of PSR-0 and PSR-4 class name to file mappings, instead of performing this lookup on the fly by the autoloader itself on runtime.
Frequently Asked Questions
Is the composer package required to install a Sugar instance?


Frequently Asked Questions
Is the composer package required to install a Sugar instance?
No. The Sugar installer ships with all required code bundled together. The installer process does not execute any Composer commands. The installer will deploy both composer.json and composer.lock in the web root directory as a reference of all dependencies which are controlled by Composer.
Why does Sugar ship ./composer.json and ./composer.lockÂ if the installer doesn't rely on them?
Composer isÂ usedÂ internallyÂ during development to manage Sugar's dependencies on third-party libraries and packages. The Composer files are shipped during development to manage Sugar's dependencies on third-party libraries and packages. The composer files are shipped with the product to give our customers the ability to expand from them.
Is the Composer package required to upgrade a Sugar instance?
No. The Sugar upgrader ships, just like the installer, with all required code bundled together. The upgrade process will validate the present Composer configuration and verify if it is compatible with the upgrade. In the case of a custom configuration, the upgrade process will report any issues which need to be resolvedÂ by theÂ system administratorÂ before the upgrade can proceed.
Why are there no wildcards in the version constraints? Doesn't composer.lock keep track of exact version numbers?
The lock file is designed to lock the dependencies to a specific version, but to protect our customers from unintentionally pulling in newer versions of dependencies owned by SugarCRM, we have chosen to use explicit version numbers in the composer.json file too.
May I change the version number of a package?
You cannot change version numbers for packages added by Sugar. Sugar will always configure exact version numbers for all of its dependencies. Changing these version numbers will result in an unsupported platform. The version constraints of packages not owned by Sugar may be modified at the developer's discretion.
Can I use Composer's autoloader?


Can I use Composer's autoloader?
We strongly recommend against using Composer's autoloader. The Sugar autoloader is fully compatible with the PSR-0 and PSR-4 autoloading recommendations from PHP-FIG, which makes the registration of an additional autoloader like the one from Composer redundant. Sugar's autoloader consumes the different mappings which are generated by Composer directly.
How can I optimize the autoloader?
Sugar ships with an optimized class map out of the box, which is pre-generated through Composer. This class map contains all different class to file mappings known to the dependencies managed by Composer. When customizing the Composer configuration, it is sufficient to run composer update --optimize-autoloader which will refresh the class map. SugarCRM's autoloader takes full advantage of this optimization.
Can I load customizations in Sugar through Composer?
Although not yet available out of the box,Â we are investigating this as a future capability.
Can I move the vendor directory out of the web root?
You cannot currently move the vendor directory, but we are investigating this as a future capability.
The Composer integration in Sugar is not flexible enough for me. What can I do?
We continuously strive to make our platform better and to facilitate both end users and developers. Our goal is to deliver a state-of-the-art environment. Do not hesitate to reach out to Sugar Support if we have overlooked your use caseÂ or if there too many constraints in the current implementation to make this a useful feature.
  	Last modified: 2023-02-03 21:04:03


Delivery and Deployment Guide for Enterprises
Overview
SugarCRM Professional Services has a set of best practices for managing instances, delivering upgraded customizations, and deploying those upgraded customizations into Sugar on-site for our Enterprise customers. The following is an example of deployment practices used by SugarCRM Professional Services team when engaged on Enterprise Sugar development projects. It does not list all possible customizations that can be made in the system, it is intended to be used as a guide for how to automate the deployment of certain types of customizations into an on-premise Sugar instance. The techniques belowÂ cannot be used withÂ Sugar's cloud service.
Deploying Application Configuration and Metadata
Use Case: Deployment of System Settings
System settings are stored in various places. In this section, we will address each type of storage for settings, and how to migrate each.
 Storage types:
config_override.php
This is a file stored in the Sugar root directory that allows for overriding core config values (found in config.php). In the UI, the main place to make changes to this is via Admin -> System Settings
database 'config' table
It is loaded, used, and accessible throughout the Sugar application through the Config API.
System Tab Settings
Forecasting Settings
Portal Settings
This is a simple key/value/category store. There aren't too many components that use this. Here are some:
Â
config_override.php
 System considerations:
File System:
config_override.php is placed in Sugar web root directoryÂ
Scripts required
PHP
You will need to write a script to read the current config_override.php and merge the existing array with the new values you'd like to change. This can be done one time, and re-used for all future config_override.php changes
Â
 Steps to migrate:
Assess the values to be changed, added, or removed


Steps to migrate:
Assess the values to be changed, added, or removed
Write a script to read the existing config_override.php, make the changes to the array, and re-write the file back to the system.
Â
database 'config' table
 System considerations:
Database:
The 'config' table stores all these values. They are stored in a very simple table schema.
Scripts required
PHP
Write a simple PHP script to use Sugar object API. See Figure 1
Â
 Steps to migrate:
Write script to use our object API for config table changes (See Figure 1)
Copy script to Sugar root directory
Execute the script
Remove the scriptÂ
 Screenshots:
Figure 1:
Â
Â
Use Case: Deployment of Reports
The customer creates a report in the Reports module. They would like to deploy that report so that end users can all access and run it.
 System considerations:
Database
Row is inserted into the Reports module (saved_reports table)
(For new team combinations) Row is inserted into the team_sets table
(For new team combinations) Rows are inserted into the team_sets_teams table
Scripts required
SQL
Retrieve the relevant rows (saved_reports, team_sets, team_sets_teams) and create a SQL script to insert them.
 Additional notes:
In the System considerations section, "new combination of teams" means that when creating the report, the end user created the Report with a set of teams that doesn't exist on any other record. This results in new entries in the team_sets and team_sets_teams tables.
 Steps to migrate:
Build a report in the dev instance
Select the database rows associated with that report (saved_reports, possibly team_sets and team_sets_teams tables)
example: "SELECT * FROM saved_reports WHERE id = 'REPORT_ID'"


example: "SELECT * FROM saved_reports WHERE id = 'REPORT_ID'"
Export the row/rows into a SQL file
Execute on the next system
Â
Use Case: Deployment of Dashboards
The customer wants to deploy the pre-built dashboards in an automated way. See Figure 1 below. This example has two dashboards, "Help Dashboard" and "My Dashboard". Each dashboard has zero or more dashlets.
 System considerations:
Database:
Row is inserted into the dashboards table for each user dashboard. The metadata column stores all the dashlets associated with that dashboard, and the assigned_user_id column stores the user who will see this dashboard.
Scripts required
PHP
After deploying the dashboards you will need to run the Quick Repair and Rebuild script found in the admin section.
Custom scripts: YES (if applying to multiple users)
Because users and id are dynamic, if applying to multiple users, you will need a custom script to retrieve those user ids and set them for each sql insert.
SQL
Script required to import the entry from the dashboards table.
 Steps to migrate:
Build a dashlet against a specific user
Select the database rows associated with that user
example: "SELECT * FROM dashlets WHERE assigned_user_id = 'USER_ID'"
Pick the dashboard you'd like to apply to other users, and export it into a SQL file
Decide what set of users you need to create the dashboard for.
Write a script to pull that list of users, dynamically set the assigned_user_id and id (id must be unique) with the insert query you exported in step 3, and run for each one of those users.
Â
 Screenshots:
Figure 1
Â
Use Case: Deployment of Roles
The customer wants to deploy the roles in an automated way. This includes creating new roles and updating previously existing roles.
 System considerations:


System considerations:
Database:
Row is inserted into the acl_roles table for each role setting. Depending on how specific the role is, we might have acl_fields and acl_actions mapped to roles through acl_roles_actions
Scripts required
PHP
Sugar has a SugarACL object API that can be used to create, read, and write roles and role definitions.Â
 Steps to migrate:
Write a script using our object API to create or write roles
Define the metadata for the changes or additions to be made
Write logic to add/update based on metadata
Execute script on dev instance and confirm changes
Use script to promote to next instance
Note: See functional sample script below
https://gist.github.com/sadekbaroudi/3191513e2bbce2170326
Â
Use Case: Deployment of Teams
The customer wants to deploy the teams in an automated way. This should be done via the Sugar object API.
 System considerations:
Scripts required
PHP
Follow steps in "Additional Notes" section below for details on building Team scripts.
TeamSets are cached per user by SugarCache. Â SugarCache should be cleared after installing new teams.
 Additional notes:
To build a script to do this, see the following:
modules/Teams/Save.php
This file is called when a user posts data through the form in the UI. This code should be replicated (until the Teams module is refactored).
modules/Teams/Team.php
function save() - this should be called as part of the save
function mark_deleted() - this should be called on the object when you want to delete a team, be sure to make sure there are no related users before doing so.
 Steps to migrate:
Write a script using our Teams object API
Create needed team object
Set appropriate data on object and/or POST data
After saving, potentially add users to the team


Set appropriate data on object and/or POST data
After saving, potentially add users to the team
Execute script on dev instance and confirm changes
Use script to promote to next instance
Â
Use Case: Deployment of User Settings
The customer wants to deploy the user settings in an automated way. There are a couple of places where we store User settings.
 Storage types:
User Preferences
This is a key value pair with a serialized and then base64 encoded value. We store many user preferences, all encoded. These are non-critical settings, and can be blown away. However, doing so will require the user to reconfigure their preferences. The data is stored in the user_preferences table. This includes data such as: Â Subpanel display order,Â Timezone preferences, etc.
Users module settings
Â These are direct values on the Users module (users table). Here we track persistent User attributes such as:Â Address,Â Phone number,Â etcÂ
User preferences
 System considerations:
Database:
Row is inserted into the user_preferences table for each user setting change.
Scripts required
PHPÂ
In order to update values with a user's preferences, you would need to write a custom script to read, update, and rewrite to the user_preferences tableÂ
 Steps to Migrate:
Write a script using our User Preferences API
Query the database to retrieve the row for a given user
base64 decode the value
unserialize the value
update the data required
serialize the data
base64 encode
rewrite the row to the database
(repeat for all applicable users)
(See modules/UserPreferences/UserPreference.php or modules/Users/User.php, specifically getPreference() and setPreference())
Load the User object
Call getPreference for the specified value
Make changes
Call setPreference for the specified value
Better performance method (direct database queries and updates):


Call setPreference for the specified value
Better performance method (direct database queries and updates):
More robust, but slower performance method (API):
Â
User table
Â
 System considerations:
Database:
Users table is updated
Scripts required:
SQL
You can directly update the Users table directly, provided the data is not encoded or encrypted (like password).
Â
 Steps to Migrate:
Write a SQL script to update values in the users table based on need
Execute script on dev instance and confirm changes
Use script to promote to next instance
Â
Use Case: Deployment of custom fields
A user wants to deploy custom fields created in an automated way. This includes anything created through Studio.
 System considerations:
File System:
Files are potentially created in the following directories:
Custom field vardef:custom/Extension/modules/<module_name>/Ext/Vardefs/sugarfield_<field_name>.php
Custom field label (and app_list_string if necessary): ./custom/Extension/modules/Accounts/Ext/Language/en_us.lang.php
Database:
The <module_name>_cstm table is created, if it doesn't already exist.
The field <field_name>_c is created against that table
Scripts required
A Quick Repair and Rebuild is required after copying the files and fields_meta_data table values.
SQL
You will need to insert the relevant entries from the fields_meta_data table
 Steps to migrate:
Export the fields_meta_data entries for the custom fields into a script
Copy the files for the custom fields
Apply #1 and #2 to another system, and execute a Quick Repair and Rebuild
Execute the DDL generated by the QRR above
Â
Use Case: Deployment of custom modules


Â
Use Case: Deployment of custom modules
A developer creates a custom module and wants to deploy it, this use case refers to a basic module, because each additional feature (logic hooks, relationships,dependencies, etc) has itsÂ  own deployment scenario.
 System considerations:
File System:
./custom/Extension/application/Ext/Include/<package_name>.php
./modules/<new_module>/*
./custom/modules/<new_module>/*
./custom/themes/default/images/*<new_module>*.(gif/png)
./custom/Extension/modules/<new_module>/*
Database:
new tables <new_module> and <new_module>_audit
Note: the DDL gets generated by the Quick Repair and Rebuild script, at which point you can execute manually or automatically
fields_meta_data table
Note: this stores all the custom fields built through Studio (not Module Builder) after the module is deployed. Make sure you retrieve all rows from this table that apply to this module and create a SQL script to insert into the next system
Scripts required
After deploying the custom module, you will need to run the Quick Repair and Rebuild script found in the admin section.
SQL
Script required to import the entry from the <new_module> and <new_module>_audit table.
Script required to import fields_meta_data table entries for this module (if there are any)
 Additional notes:
For a full custom module deployment scenario, this deployment scenario should be ran first then all of the extended module features deployment scenarios should be run:
 Steps to migrate:
Copy all files listed in file system section above
Export fields_meta_data table entries as apply to the custom module (if any)
Run Quick Repair and Rebuild
Either manually or automatically run the DDL output from QRR
Test functionalityÂ
Use Case: Deployment of custom Relationships


Test functionalityÂ
Use Case: Deployment of custom Relationships
A user wants to deploy custom relationships created in an automated way. This includes anything created through Studio.Â
Follow the same instructions as for CustomÂ Fields, but:
Ignore the fields_meta_data table
Be sure to consider the following:
custom/Extension/modules/<side_1_of_relationship>/Ext/
custom/Extension/modules/<side_2_of_relationship>/Ext/
custom/Extension/modules/relationships/
Otherwise, the same process applies.
Use Case: Deployment of custom View or Layout metadata (Web)
The user creates custom layouts and views for web from studio and wants to deploy them.
 System considerations:
File System:
Layouts:
./custom/modules/<module>/clients/<platform>/layouts/<layout>/ <layout>.php
Views
Creating a layout from Studio actually augments the Sidecar view metadata instead of Sidecar layout metadata
./custom/modules/<module>/clients/<platform>/views/<layout>/ <layout>.php
Scripts required
After deploying you will need to run the Quick Repair and Rebuild script found in the admin section.Â
 Additional notes:
For layouts(record) you have the option to simply save the modified layout. In this case the metadata for the layout can be found in ./custom/working/modules/<module>/<platform>/views/<view>/<view>.php
Layouts created from studio create views in .custom/modules/<module>/viewsThe created views can be record | list | selection-list
For the two popup layouts(created from studio), extra metadata is provided in the ./custom/modules/<module>/metadata/popupdefs.php
Â
Use Case: Deployment of custom View or Layout metadata (mobile)
The user creates custom layouts and views for mobile from studio and wants to deploy them.
 System considerations:
File System: YES
Layouts:


System considerations:
File System: YES
Layouts:
./custom/modules/<module>/clients/mobile/layouts/<layout>/ <layout>.php
Views
./custom/modules/<module>/clients/mobile/views/<view>/ <view>.php
Database: NO
Scripts required
After deploying you will need to run the Quick Repair and Rebuild script found in the admin section.
 Additional notes:
Layouts and views are handled the same as with web. From Studio, you can augment detail, edit and list views.
Deploying Application Code and Integrations
Use Case: Deployment of custom CSS (LESS)
Â
In order to update branding, developers can deploy customized CSS.
 System considerations:
File System: YESÂ
./custom/themes/custom.less
Scripts required
You will need to run the Quick Repair and Rebuild script found in the admin section to rebuild the Sugar CSS bundles.
Use Case: Deployment of Logic Hooks and Web Logic Hooks
The developerÂ creates custom logic hooks, they want to deploy them.
 System considerations:
 Logic Hook:
File System: YESÂ
application hooks :
./custom/Extension/application/Ext/LogicHooks/<file>.php
module specific hooks:
./custom/Extension/modules/<module>/Ext/LogicHooks/<file>.php
Scripts required
You will need to run the Quick Repair and Rebuild script found in the admin section to rebuild the extensions.
  Web Logic Hook:
Database: YES
Only for weblogic hooks : row is inserted into the weblogichooks table.Â
Scripts required
After deploying the custom hooks and database entry in the weblogichooks table, you will need to run the Quick Repair and Rebuild script found in the admin section.
SQL
Script required to import the entry from the weblogichooks table.
Use Case: Deployment of custom API endpoints


Use Case: Deployment of custom API endpoints
The user creates a custom api endpoints, he wants to deploy them.Â
 System considerations:
File System
clients/<platform>/api/*
modules/:module/clients/<platform>/api/*
custom/clients/<platform>/api*
custom/modules/<module>/clients/<platform>/api/*
Scripts required
After deploying the custom api you will need to run the Quick Repair and Rebuild script found in the admin section. This will rebuild the ./cache/file_map.php and ./cache/include/api/ServiceDictionary.rest.php files to makeÂ your endpoint available.
 Additional notes:
Logic for how api endpoints are loaded, can be found in ./include/api/ServiceDictionary.php (where api endpoints are loaded from, how they are built on Quick Build and Repair, etc.)
Use Case: Deployment of custom Administration Panels
The user creates custom administration panels, and wants to deploy them.
 System considerations:
File System:
./custom/Extension/modules/Administration/Ext/Administration/<file>.php
./custom/Extension/modules/Administration/Ext/Language/<langtype.name>.php
./custom/themes/default/images/<icon_image_name>.<img_extension>
 and depending on the    admin panel url, either   :
./custom/modules/<linkUrlModule>/*
or
./custom/modules/<linkUrlModule>/clients/base/layouts/<route_name>/*
./custom/modules/<linkUrlModule>/clients/base/views/<route_name>/*
Scripts required
After deploying the admin panels, you will need to run the Quick Repair and Rebuild script found in the admin section.
 Additional notes:
The admin url can specify new sidecar routes, old bwc routes, edit view files, plain scripts, or just open a drawer. Determining the additional resources to be copied may be impossible without a standard in place.


Use Case: Deployment of custom Jobs / Schedulers
The user creates custom jobs and schedulers, and wants to deploy them.
 System considerations:
File System: YES:
./custom/Extension/modules/Schedulers/Ext/ScheduledTasks/<jobname>.php
./custom/Extension/modules/Schedulers/Ext/Language/<langtype.jobname>.php
Database: YES
Row is inserted into the schedulers table, if a job is added as a scheduled job usingÂ Administration > Scheduler.
Scripts required
After deploying the custom jobs, you will need to run the Quick Repair and Rebuild script found in the admin section.
SQL
Script required to import the entry from the schedulers table.
  	Last modified: 2023-02-03 21:04:03


Sugar 12.3 to 13.0 Migration Guide
Overview
The purpose of this document is to provide insight to Sugar Developers for upgrading custom Sugar code, extensions, and integrations to the Sugar 13.0 (Q2 2023) release.Â
Cloud and On-Site Sugar Release
For those looking to upgrade from Sugar 12.0, you will be catching up with additional content released in the 12.1 (Q3 2022), 12.2 (Q4 2022), and 12.3 (Q1 2023) cloud-only Sugar releases. In addition to this guide, please review theÂ Sugar 12.1,Â Sugar 12.2, andÂ Sugar 12.3Â migration guides and the releaseÂ notes linked below to get a full view of changes since Sugar 12.0.
REST API Version Number
The current version for the Sugar REST API is 11_20.Â
Changes That May Affect Developers
The changes inÂ Sugar 13.0 (Q2 2023) that could cause an immediate impact on customizations and integrations that were built for earlier versions of Sugar are highlighted in the following Developer Blog post in the SugarClub community:Â
Sugar 13.0 (Q2 2023) Customization Guide
Related Documents
For more information about what changed in Sugar 13.0.x, please refer to the following related documents:
What to Expect When Upgrading to Sugar 13.0 (Q2 2023)
13.0.0 Upgrade Paths
13.0 Installation and Upgrade Guide
13.0.x Supported Platforms
Release Notes
Sugar Sell 13.0.0 Release Notes
Sugar Serve 13.0.0 Release Notes
Sugar Enterprise 13.0.0 Release Notes


Sugar Enterprise 13.0.0 Release Notes
  	Last modified: 2023-04-05 00:05:28


Development Methodology
Overview
This pageÂ discussesÂ standardÂ practices that we recommend for improving the success rates ofÂ SugarÂ development projects.Â
Development Best Practices
When developing SugarÂ® customizations as part of an on-site CRM project implementation, we recommended placing the entire Sugar application filesystem under source code management. Sugar developers know that customizations made to Sugar are placed under the ./custom/ directory. But during the lifecycle of a CRM implementation, you will need to upgrade Sugar versions, which will change core files. Many projects will also need to track other related project files that may not all be Sugar platform code. For example, pre-flight SQL scripts, data migration scripts, Web server configuration settings, etc.
For SugarCloud projects and ISVs, if you are building a custom module package or integration designed to be installed into many Sugar instances (including SugarCloud instances), then tracking only ./custom/ directory files should be enough.
Using .gitignore Files
Today, many developers choose to use Git as their source control management. There are certain Sugar application files that you do not want to track; most of these are generated files that are created at runtime or are Sugar instance-specific configuration files.
Below is a sample .gitignore file that you can use or adapt to the source control management system of your choice.
*.log
/.htaccess
/config.php
/config_override.php
/cache
/upgrades/module
/upload
/custom/blowfish
/custom/history
/custom/application/Ext
/custom/modules/*/Ext
/custom/Extension/**/*orderMapping.php
Recommendations for Development Teams
Code quality is important to maintain because Sugar customizations run in the same environment as the rest of the Sugar application. Here are a few best practices to helpÂ development teams uphold code quality.
Adopt an appropriate Git workflow for development. For reference, see Atlassian's tutorial on Git workflow options.


Develop within feature branches that are tested before being merged back into master to keep master stable.
Avoid workflows that involve developers committing directly into the master branch to preventÂ code destabilization.
Development teams should always perform code reviews before merging in new code.
Deploying Sugar Code
Where you plan to deploy Sugar code is the biggest factor in determining how Sugar code should be deployed and how your project should be managed.
Are you working on a Sugar project for an on-premise Sugar implementation? Are you working on a custom module that you plan on distributing through SugarExchange? Are you planning a solely REST API integration? The answers to these questions guide how you should develop and deploy Sugar code.
Sugar on-siteÂ projects :Â Develop these customizations using the exact version and flavor of Sugar that you plan to use in production.
SugarCloudÂ projects :Â Develop these customizations on the latest available version of Sugar for the particular flavor the customer has purchased.
Custom modules or integrations : If you plan on distributing your customization to many Sugar customers via SugarExchange or channel partners,Â design your customization with Sugar's cloud service in mind.
Sugar's cloud service is more restrictive than our on-site installs regarding supported customizations.
A customization designed for Sugar's cloud service canÂ be supported in Sugar on-site instances, but the inverse is not always true.
For more information on Sugar's cloud service restrictions, please refer to the SugarCloud Policy Guide.
Packaging
The packaging of customizations is not a concern for many Sugar projects. Many projects just use Git (or some other file version control) to manage the distribution and deployment of Sugar code customizations. However, there are situations where the packaging of Sugar customizations is necessary.


If you plan on distributing Sugar custom code, then you must package your customization as a Module Loadable PackageÂ (a .zip file that includes all custom code along with a manifest file). It is easy to write a script to build a module loadable package either from custom directory content or by extracting customizations out of a development environment. See examples on Github here and here.
Note: Sugar Sell Essentials customers do not have the ability to upload custom file packages to Sugar using Module Loader.
In some Enterprise environments, changes are tightly controlled, and ownership of various Sugar application components may be spread across multiple teams, requiring a coordinated deployment. For example, a Database Administrator (DBA) may be responsible for implementing database schema (DDL) changes and a System Administrator may be responsible for implementing file system changes.
For these situations:
File system changes can be accounted for using Git to determine the difference between current production state and the latest changes to be deployed.
DDL changes can be accounted for via deploying latest file system changes on a clone of Sugar production instance and running Quick Repair command. Sugar will prompt you with any DDL changes that need to be made that you can then capture and share with your DBA.
Data Manipulation Language (DML) changes, if necessary, should be managed within SQL or PHP scripts.
Deployment
If using the traditional Git-based deployment, then deploying new Sugar code is as easy as checking out the latest branch and then running Quick Repair and Rebuild. Run the quick repair from the Sugar user interface, orÂ scriptÂ itÂ for fully automated deployment.
When deploying to an instance on Sugar's cloud service, it is necessary to installÂ the packageÂ manually using Module Loader. It is not possible to automate the deployment of packages into instances on Sugar's cloud service.


In a coordinated deployment, database changes should be deployed first, followed by filesystem changes, followed by a Quick Repair (if permitted) to clear system caches. You can clear caches manually by deleting the contents of the ./cache/ directory and then truncate the metadata_cache table in the Sugar database. The Sugar application regenerates these caches as needed.
Using Sugar Studio to deploy changes into new environments (especially Production) is not recommended. Manually re-creating customizations using the Studio user interface can be error-prone. It also runs the risk ofÂ inadvertentlyÂ overwriting other code customizations. It may be initially slower to create a custom field, etc.Â manually using extensions on filesystem but, in theÂ long run, you benefit from better change management and automation.
Managing Multiple Environments
CRMs are business-critical applications so development should never be performed directly on your production environment. A typical Sugar project involves multiple staging environments as well as individual development environments that each Sugar developer uses for actual coding.
SugarCRM recommends 4 different environments:
Production environment : used by real users
User Acceptance Test (UAT) environment : used by business stakeholders to sign off on changes that go into production
Quality Assurance environment : used for test and validation of new features and bug fixes
Development environments : used by individual Sugar developers to create and test code (often running on a local laptop or PC)
Code changes that flow upstream from a development environment should not be allowed into production without going through quality assurance (QA) and user acceptance testing (UAT) first. The intermediate environments serve as gates between development and production that helpÂ intercept problems before they reach production.
User and CRM data that needs to flow downstream from production environment should pass through intermediate environments as well to ensure consistency and that it is cleaned or anonymized of any personally identifiable or sensitive information.
Consistency


Consistency
Maintaining as much consistency as possible for each of these environments is essential. Inconsistent environments can create issues where bugs are reproducible in one environment and not others (for example, a bug that only appears in production). Many times, these bugs are traced to configuration parameters that are not directly related to Sugar or the features and customizations under development.
To replicate your production environment as accurately as possible,Â we recommend you use VM or container technology in your development and QA environments.
Your UAT environment should match the infrastructure of your production environment.
SugarCRM uses a variety of container technologies in developing the core Sugar application and for working on Sugar projects. In particular, we use Virtual Box, VMWare, Amazon AMIs, Docker containers, Vagrant, and Packer. SugarCRM Engineering also uses Puppet to centrally manage the provisioning and configuration of all these different environments.
Testing
SugarCRM recommends using a variety of testing methods to ensure the quality of your Sugar project. Perform unit testing, functional testing (either manual or GUI automation), system integration testing, user acceptance testing, and performance testing.
Unit testing should be applied to ensure that even the smallest code units within your application are behaving as expected.
Functional testing should be performed to ensure that each feature and function behaves the way it was designed.
System Integration testing should be performed to ensure that Sugar co-exists with external systems and that data flows between all these systems successfully.
User acceptance testing should be performed by key stakeholders to ensure that your project is meeting business requirements.
Performance testing should be performed to ensure that the Sugar application's responsiveness meets user expectations and that the application continues to scale.
In our experience, neglecting any of these tests can negatively impact a Sugar project in terms of maintainability, customer satisfaction, and business success.Â
In the next section, we introduce some tools and open-source resources that can help you start a QA practice for your project.