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.');
                    }
                }                  
             }
         }                         
    }
}

Salesforce Apex Trigger Variable Availability to Events

Trigger.new Trigger.newMap Trigger.Old Trigger.OldMap
Before Insert Yes No No No
Before Update Yes Yes Yes Yes
Before Delete Yes Yes Yes Yes
After Insert Yes Yes No No
After Update Yes Yes Yes Yes
After Delete No No Yes No
After Undelete Yes Yes No No

Rollup Summary Trigger for a Lookup Relationship

Here is a sample Rollup Summary calculation trigger for Lookup relationship. In below example I’m using Account object as parent and Contact object as child.

I’m updating parent object Account record field Total__c with Sum of all related child object Contact records Amount__c field value.

Apex Trigger:

trigger RollUpFromChildToParent on Contact (after insert, after update, after delete, after undelete) {
    
    Set<Id> accountIds = new Set<Id>();
    List<Account> accList = new List<Account>();
    
    if(Trigger.isInsert || Trigger.isUpdate || Trigger.isUndelete){
        for(Contact con : Trigger.new){
            if(con.AccountId != null){
                accountIds.add(con.AccountId); 
            }
        }
    }
    
    If(Trigger.isDelete){
        for(Contact con : Trigger.old){
            if(con.AccountId != null){
                accountIds.add(con.AccountId);
            }
        }
    }
    
    for(AggregateResult aRes : [SELECT AccountId, SUM(Amount__c) Total FROM Contact WHERE AccountId IN :accountIds GROUP BY AccountId]) {
        accList.add(new Account(Id = (Id)aRes.get('AccountId'), Total__c = (Decimal)aRes.get('Total')));
    }
    
    try{
        Update accList;
    }catch(DmlException de){
        System.debug(de);
    }
}

Salesforce Apex Trigger Events

  • Before Insert: Trigger that executes due to an insert operation and before the record is saved to the database it is a before Insert trigger
  • Before Update: Trigger that executes due to an update operation and before the record is saved to the database it is a before update trigger
  • Before Delete: Trigger that executes due to a delete operation and before the record is saved to the database it is a before delete trigger
  • After Insert: Trigger that executes due to an insert operation but after the record is saved to the database it is an after Insert trigger
  • After Update: Trigger that executes due to an update operation and after the record is saved to the database it is an after update trigger
  • After Delete: Trigger that executes due to a delete operation and after the record is deleted from the database it is an after delete trigger
  • After Undelete: Trigger that executes due to a undelete operation and after the record is undeleted from the database it is an after undelete trigger

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';  
        }
    }
}