function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
SandySandy 

How to combine two Triggers on same object ?

Hi!
Please can someone assist me with two triggers that I am trying to get working on for the Case object. 
first trigget is for automatically assign entitlements to cases from email or web. and second one is complete resolution time when case is closed.
Here are the two triggers as currently written - appreciate that they probably need work! :-)
Thanks in advance.

Trigger 1
trigger DefaultEntitlement on Case (Before Insert, Before Update) {
    Set<Id> contactIds = new Set<Id>();
    Set<Id> acctIds = new Set<Id>();
    for (Case c : Trigger.new) {
        contactIds.add(c.ContactId);
        acctIds.add(c.AccountId);
    }
    List <EntitlementContact> entlContacts = 
                [Select e.EntitlementId,e.ContactId,e.Entitlement.AssetId 
                From EntitlementContact e
                Where e.ContactId in :contactIds
                And e.Entitlement.EndDate >= Today 
                And e.Entitlement.StartDate <= Today];
    if(entlContacts.isEmpty()==false){
        for(Case c : Trigger.new){
            if(c.EntitlementId == null && c.ContactId != null){
                for(EntitlementContact ec:entlContacts){
                    if(ec.ContactId==c.ContactId){
                        c.EntitlementId = ec.EntitlementId;
                        if(c.AssetId==null && ec.Entitlement.AssetId!=null)
                            c.AssetId=ec.Entitlement.AssetId;
                        break;
                    }
                } 
            }
        } 
    } else{
        List <Entitlement> entls = [Select e.StartDate, e.Id, e.EndDate, 
                e.AccountId, e.AssetId
                From Entitlement e
                Where e.AccountId in :acctIds And e.EndDate >= Today 
                And e.StartDate <= Today];
        if(entls.isEmpty()==false){
            for(Case c : Trigger.new){
                if(c.EntitlementId == null && c.AccountId != null){
                    for(Entitlement e:entls){
                        if(e.AccountId==c.AccountId){
                            c.EntitlementId = e.Id;
                            if(c.AssetId==null && e.AssetId!=null)
                                c.AssetId=e.AssetId;
                            break;
                        }
                    } 
                }
            } 
        }
    }
}

Trigger 2

trigger CompleteResolutionTimeMilestone on Case (before update) {
    if (UserInfo.getUserType() == 'Standard'){
        DateTime completionDate = System.now(); 
            List<Id> updateCases = new List<Id>();
            for (Case c : Trigger.new){
                    if (((c.isClosed == true)||(c.Status == 'Closed'))&&((c.SlaStartDate 
                        <= completionDate)&&(c.SlaExitDate == null)))
                   updateCases.add(c.Id);
                c.EntitlementId = null;
        }
    if (updateCases.isEmpty() == false)
        milestoneUtils.completeMilestone(updateCases, 'Resolution Time', completionDate);
    }
}
Best Answer chosen by Sandy
Ronald Van AkenRonald Van Aken
Hi Sandy,

You might have perfectly rational reasons why you have 2 triggers, but...  Your triggers overlap in the "before update" event.  You won't know which will fire first, and they might get queued in a way that it's not always the same.  If timing is important, you will have to add complexity to control it.  Unless this all works perfectly and you're sure you'll never add anything or have to perform maintenance, I'd fix merge the triggers first.  Maybe that's where you're headed.

The other thing that seems sub-optimal is the amount of work being done in the trigger.  This adds to your grief to merge the triggers.

I would recommend three things:
1. Use a single trigger per object (trigger CaseSave on Case)
2. Only qualify the record type (or similar inclusion/exclusion criteria)
3. Do all the work in a class or classes with helpful names (CaseEntitlement, CaseMilestone)

The trigger would look something like this:
trigger CaseSave on Case (Before Insert, Before Update)
{
    List<Case> caseList = new List<Case>();

    // You don't need this iteration if your trigger applies 
    // to all cases (pass Trigger.new to classes)...
    for (Case c : Trigger.new)
    {
        // wrap in "if..." to qualify cases by RecordType.Name if necessary
        caseList.add(c);
    }

    if (!caseList.isEmpty())
    {
        CaseEntitlement.AssignDefault(caseList);

        if (Trigger.isUpdate)
        {
            CaseMilestone.CompleteResolutionTime(caseList);
        }
    }
}
Your two classes would look like this:
CaseEntitlement.cls
public without sharing class CaseEntitlement
{
    public static void AssignDefault(List<Case> caseList)
    {
        // Your code goes here...
    }
}
CaseMilestone.cls
public without sharing class CaseMilestone
{
    public static void CompleteResolutionTime(List<Case> caseList)
    {
        // Your code goes here...
    }
}
With this example you gain the benefit of controlling the timing, and filtering the cases if necessary.  Each class method would execute on a list of cases similar to your existing trigger code.  After your classes run, Salesforce will attempt to commit the data.  I didn't really look at the details of your code since you were just asking about how to merge your triggers.

Please let me know if this solves your issue.

Best regards,
Ron

 

All Answers

SwethaSwetha (Salesforce Developers) 
HI Sandhya,
You will need to use Trigger Context Variables and write a handler class to merge the triggers.

See examples provided in below articles to get started https://medium.com/elevate-salesforce/apex-trigger-framework-a-generic-way-to-join-tigger-contexts-46c4d9277db0
https://salesforce.stackexchange.com/questions/132421/combine-two-triggers
https://salesforce.stackexchange.com/questions/31535/merging-two-triggers-into-one

Thanks
Ronald Van AkenRonald Van Aken
Hi Sandy,

You might have perfectly rational reasons why you have 2 triggers, but...  Your triggers overlap in the "before update" event.  You won't know which will fire first, and they might get queued in a way that it's not always the same.  If timing is important, you will have to add complexity to control it.  Unless this all works perfectly and you're sure you'll never add anything or have to perform maintenance, I'd fix merge the triggers first.  Maybe that's where you're headed.

The other thing that seems sub-optimal is the amount of work being done in the trigger.  This adds to your grief to merge the triggers.

I would recommend three things:
1. Use a single trigger per object (trigger CaseSave on Case)
2. Only qualify the record type (or similar inclusion/exclusion criteria)
3. Do all the work in a class or classes with helpful names (CaseEntitlement, CaseMilestone)

The trigger would look something like this:
trigger CaseSave on Case (Before Insert, Before Update)
{
    List<Case> caseList = new List<Case>();

    // You don't need this iteration if your trigger applies 
    // to all cases (pass Trigger.new to classes)...
    for (Case c : Trigger.new)
    {
        // wrap in "if..." to qualify cases by RecordType.Name if necessary
        caseList.add(c);
    }

    if (!caseList.isEmpty())
    {
        CaseEntitlement.AssignDefault(caseList);

        if (Trigger.isUpdate)
        {
            CaseMilestone.CompleteResolutionTime(caseList);
        }
    }
}
Your two classes would look like this:
CaseEntitlement.cls
public without sharing class CaseEntitlement
{
    public static void AssignDefault(List<Case> caseList)
    {
        // Your code goes here...
    }
}
CaseMilestone.cls
public without sharing class CaseMilestone
{
    public static void CompleteResolutionTime(List<Case> caseList)
    {
        // Your code goes here...
    }
}
With this example you gain the benefit of controlling the timing, and filtering the cases if necessary.  Each class method would execute on a list of cases similar to your existing trigger code.  After your classes run, Salesforce will attempt to commit the data.  I didn't really look at the details of your code since you were just asking about how to merge your triggers.

Please let me know if this solves your issue.

Best regards,
Ron

 
This was selected as the best answer