Extending SugarCRM Logic

Introduction

This article will walk through how to create and use SugarCRM custom expressions to extend Sugar Logic.

Sugar Logic enables administrators to create business logic for field calculations without needing to write PHP code. The field’s dynamic value will be automatically updated based on a formula you provide. Administrators can create these formulas via Studio or Module Builder when editing a field by selecting the Dependent checkbox and using formula builder. Dependent Fields can also use Sugar Logic to control the fields visibility or editable status so that they are only shown or editable when certain conditions are met.

i.e. we will make the custom Status field in the Opportunities module appear only if the Account Type field equals one of the listed specified values: Manager, Supervisor.

isInList($account_type, createList("Manager","Supervisor"))

The dependent formula above will make the Status field visible in the edit and detail view of the Opportunities module only if the selected value of the dropdown Type field equals either Manager or Supervisor.

This approach is great for simple logic, but when formulas get more complex or are reusable in multiple fields, maintenance can become very time-consuming. Let us presume that we use above simple logic on 30 fields and customer want to add another account type, we need to change it on 30 places.

Custom expressions can be used instead of a Sugar Logic formula for a dependent field. They solve a problem with maintenance by keeping all logic in one PHP file. So changing logic on one place apply to every usage. They also provide an interface to create very complex logic which can’t be achieved by using Sugar Logic.




Example

In this example we will make specific fields read-only on the Opportunities module depending of Opportunity status and assigned user. This can be done on dependencies using Sugar logic but implementing all in many different dependency files can be hard to maintain. A better solution is to create new Sugar Expression which will handle all logic on one place and just return TRUE or FALSE.

Let us presume that Opportunities:

  • can have different statuses: New, Open, Processed, Closed.
  • have some fields that can be edited only by two user groups: Manager, Supervisor all other users see this fields as Read Only
  • fields are editable by Manager only if status is not Closed and always for Supervisor

So instead of adding all this conditions to dependencies we will create one function with one parameter – Opportunity status.




Expression

In /sugar/custom/include/Expressions/Expression/Boolean/ , a new PHP file is added named checkLogicExampleExpression.php.

It is important that name is set same as the class name and that it extends BooleanExpression. So all logic is contained in this PHP file and we can use checkLogicExample() as any Sugar Logic function.


require_once("include/Expressions/Expression/Boolean/BooleanExpression.php");
 
class checkLogicExampleExpression extends BooleanExpression    {
 
    function evaluate()
    {
        global $log, $current_user;
        $params = $this->getParameters();
        $status = $params[0]->evaluate();
 
        // exception handling
        if(empty($status))
        {
            throw new Exception("checkLogicExampleExpression: Function require parameter");
        }
 
        // check if status is one of supported, if no return false
        $acceptedStatus = array("New", "Open", "Processed", "Closed");
        if (!in_array($status, $acceptedStatus))
        {
            $log->error("checkLogicExampleExpression() Status is different then allowed.");
            return AbstractExpression::$FALSE;
        }
 
        // get user teams
        $userTeams = $current_user->get_my_teams();
        $log->info("$current_user->name is a member of the following Teams: " . print_r($userTeams, true));
 
        // For manager fields are available only if status is not Closed
        if(in_array("Manager", $userTeams) && ($status != "Closed"))
        {
            return AbstractExpression::$TRUE;
        }
        else if(in_array("Supervisor", $userTeams))
        {
            return AbstractExpression::$TRUE;
        }
 
        return AbstractExpression::$FALSE;
    }
 
    // Define the javascript version of the function
    static function getJSEvaluate()
    {
        return <<<<EOQ
                var params = this.getParameters();
                var status = params[0].evaluate();
   
                if((status != "New")&&(status != "Open")&&(status != "Processed")&&(status != "Closed")){
                    console.log("Status value is not supported "+status);
                    return SUGAR.expressions.Expression.FALSE;
                }
   
                var userTeams = SUGAR.App.user.get('my_teams');
                var ret = 0;
                _.each(userTeams, function(userTeam) {
                    if (userTeam.name == "Supervisor") {
                        ret = 1;
                        return false;
                    }else if(userTeam.name == "Manager"){
                        ret = 2;
                        return false;   
                    }
                });
      
                if((ret == 2) && (status != "Closed")){
                    return SUGAR.expressions.Expression.TRUE;
                } else if(ret == 1){
                    return SUGAR.expressions.Expression.TRUE;
                }
                 
                return SUGAR.expressions.Expression.FALSE;
                 
                 
EOQ;
    }
 
    static function getParameterTypes() {
        return AbstractExpression::$STRING_TYPE;;
    }
 
 
    static function getParamCount()
    {
        return 1;
    }
 
    static function getOperationName()
    {
        return "checkLogicExample";
    }
}

Function evaluate() and their javascript equivalent getJSEvaluate() are used. First, we get a parameter that is sent to function and check it. If is not one of the valid statuses return an exception. Then we get all user teams. If a user is a member of team Manager and status is not Closed we return true, also if a user is a member of team Supervisor we return true. In all other cases, false will be returned.

Other functions are:

  • getParameterTypes() – must return parameter types
  • getParamCount() – must return number of parameters
  • getOperationName() – return operation name that will be used in sugar logic, note that Expression part is removed from operation name

So now we have all logic in one class and we can use expression checkLogicExample in dependencies. Also, this expression will be available in Studio for administrators to use.

After creating custom Expression is mandatory to go to Administration, click on Repair and then Rebuild Sugar Logic Functions.




Dependencies

So now, in the dependencies PHP file, we can check if a user can modify fields.

File depend_fields.php is added in /sugar/custom/Extension/modules/Opportunities/Ext/Dependencies/

For trigger we will use checkLogicExample and send parameter $status which is Opportunity module field. It will be executed on page load and every time status has been changed.

In actions we put what will happen if checkLogicExample returns true. In this example, it will remove read-only from opportunity_type, status and custom_action fields. If it returns false notActions will be executed, setting same fields to read-only.


$dependencies['Opportunities']['depend_fields'] = array(
    'hooks' => array("edit","view"),
    'trigger' => 'checkLogicExample($status)',
    'triggerFields' => array('status'),
    'onload' => true,
    'actions' => array(
        array(
            'name' => 'ReadOnly',
            'params' => array(
                'target' => 'opportunity_type',
                'value' => 'false'
            )
        ),
        array(
            'name' => 'ReadOnly',
            'params' => array(
                'target' => 'status',
                'value' => 'false'
            )
        ),
        array(
            'name' => 'ReadOnly',
            'params' => array(
                'target' => 'custom_action',
                'value' => 'false'
            )
        ),
         
    ),
 
    'notActions' => array(
        array(
            'name' => 'ReadOnly',
            'params' => array(
                'target' => 'opportunity_type',
                'value' => 'true'
            )
        ),
        array(
            'name' => 'ReadOnly',
            'params' => array(
                'target' => 'status',
                'value' => 'true'
            )
        ),
        array(
            'name' => 'ReadOnly',
            'params' => array(
                'target' => 'custom_action',
                'value' => 'true'
            )
        ),
    ),
);

Conclusion

This is an easy example that demonstrates customization of sugar logic. It is good practice to use custom expressions in dependencies if advanced logic is needed or if Sugar Logic needs to be applied on a large number of fields.

Read about SugarCRM Logic Hooks here or check our other articles.

Check our Youtube channel here.

Marko Vucak Author

Leave a Reply

Your email address will not be published. Required fields are marked *