Tag Archives: Apex Trigger

Restrict multiple approval for a single user to a single record in Salesforce

I had a requirement to restrict multiple approval for a single user to a single record. There were multiple steps of approval process and the approver may be in different queue or as a manager for a user.

So, I implemented it using a trigger. In this article I’ll demonstrate how to implement it.
Here Application__c is the object, on which 3 steps approval process has implemented.
A custom check box field Check_Approver__c has set value to true, in all 3 steps field update action.

In below trigger I filtered the ProcessInstanceStep object records with current user Id in ActorId and the object Id (here object Id means record Id) in ProcessInstance.TargetObjectId.

  • If an User will approve or reject a record, then the SOQL query will return a single record.
  • If the SOQL query return a record, then trigger will throw an error message.

Trigger source code:

trigger ApplicationTrigger on Application__c (before Update)
{
    if(trigger.isUpdate){

        Id currentUserId = UserInfo.getUserId();
        for(Application__c sf: trigger.new){   
                  
            if(sf.Check_Approver__c == true){
              
                List<processinstancestep> existingApprovals = [SELECT ActorId
                                    FROM ProcessInstanceStep WHERE ProcessInstance.TargetObjectId = :sf.Id
                                    AND (StepStatus = 'Approved' OR StepStatus = 'Rejected')
                                    AND ActorId = :currentUserId];
                                      
                if(existingApprovals != null){
                  
                    if(existingApprovals.size() > 0){

                        sf.addError('You have already approved or rejeted the record.');
                    }
                }                  
             }
         }                         
    }
}

Compare Old And New Values In Salesforce Trigger

Salesforce provides trigger.oldmap where in all the old records are stored in map with keyset as their ids. We can compare old field value of records with the new values in trigger.

Here in the below example, the trigger compares the account number old value with the new value. If the account number is changed the trigger assigns the type field value “Customer – Direct” else it assigns it a value “Customer – Channel”.

trigger AccountTrigger on Account (before update) {
    
    for (Account acc: Trigger.new) {
        Account oldAccount = Trigger.oldMap.get(acc.Id);
        if(acc.AccountNumber != oldAccount.AccountNumber) {
            System.debug('--Account Number is changed--');
            System.debug('--Old Account Number -' + oldAccount.AccountNumber);
            System.debug('--New Account Number -' + acc.AccountNumber);
            acc.Type = 'Customer - Direct';
        }
        else{
            System.debug('--Account Number has not been updated--');
            acc.Type = 'Customer - Channel';  
        }
    }
}

Bulkify Apex Trigger in Salesforce

Bulkifying Apex Trigger refers to the concept of making sure the code properly handles more than one record at a time. When a batch of records initiates Apex, a single instance of that Apex code is executed, but it needs to handle all of the records in that given batch.

For example, a trigger could be invoked by an Force.com SOAP API call that inserted a batch of records. So if a batch of records invokes the same Apex trigger, all of those records need to be processed as a bulk, in order to write scalable code and avoid hitting governor limits.

Here is a simple example of some Apex code both bulkified, and not bulkified.

Not Bulkified:

trigger AccountTriggr on Account (before update) {
    
    //This only handles the first record in the Trigger.new collection
    //But if more than one Account initiated this trigger, those additional records
    //will not be processed
    Account acc = Trigger.new[0];
    List<Contact> conList = [SELECT Id, FirstName, LastName, Email, Phone FROM Contact WHERE AccountId = :acc.Id];
}

In above apex trigger the issue is that only one Account record is handled because the code explicitly accesses only the first record in the Trigger.new collection by using Trigger.new[0]. Instead, the trigger should properly handle the entire collection of Accounts in the Trigger.new collection.

Bulkified:

trigger AccountTriggr on Account (before update) {
    
    //Loop through all records in the Trigger.new collection
    for(Account a: Trigger.new){
        //Write your logic
    }
}

In above bulkified apex trigger the code iterates across the entire Trigger.new collection with a for loop. Now if this trigger is invoked with a single Account or up to 200 Accounts, all records are properly processed.

Call Batch Apex From Apex Trigger

Batch Apex Class:

global class AccountBatchApex implements database.batchable<sobject>{
    
    global database.querylocator start(database.batchableContext bc){
        String query = 'SELECT Name, AccountNumber, Type From Account';
        return database.getquerylocator(query);
    }
    
    global void execute(database.batchablecontext bd, List<Account> scope){
        for(Account acc: scope){
            //Write your logic
        }  
    }
    
    Public void finish(database.batchableContext bc){ 
    }
}

Apex Trigger:

trigger AccountTrigger on Account (after insert) {
    List<Account> accList = new List<Account>();
    for(account acc : trigger.new){
        if(acc.Type.equals('Customer - Direct')){
            accList.add(acc);
        }
    }
    
    if(accList.size() > 0){
        AccountBatchApex objBatch = new AccountBatchApex();
        Database.executebatch(objBatch,200);
    }
}

Different Ways of Making a Field Mandatory in Salesforce

There are 4 ways of making the field mandatory:

Page Layout: Field can be made mandatory from the page layout when it needs to be made mandatory for a set of users.
Field Level Security: Field can be made mandatory from the FLS when it needs to be made mandatory for all the users in the Organization and even from the API’s.
Validation Rule: Field can be made mandatory from the Validation Rule, when it needs to be made mandatory for user who is using the same Page layout used by other users.
Before Triggers: we can also make a field mandatory using before trigger

Note: Salesforce.com recommends using the Page Layout option for making the field mandatory.