+ Start a Discussion
Salesforce2015Salesforce2015 

Trigger Help

Hi Experts,
 
I need a trigger help.
Two objects having lookup relationship.
Parent Object - Case
Child Object - Candidate_Task__c
Child object having one custom number field   Resource_incurred_hours_Update__c.
Parent object having one custom number field   Total_Hours_Update__c.
I need to calculate the sum of all “Resource incurred hours” field values and populate in “Total Hours” field.
Please find the below image and trigger.

Case Object and Custom Object (Project - Task - Resource):

Case and Custom Object (Project - Task - Resource)

Trigger:

trigger RollUpIncurredHours on Candidate_Task__c (after delete, after insert, after update) {
 
  //Limit the size of list by using Sets which do not contain duplicate elements
  set<iD> ProjectTaskResourceIds = new set<iD>();
 
  //When adding new Incurred Hours or updating existing Incurred Hours
  if(trigger.isInsert || trigger.isUpdate){
    for(Candidate_Task__c p : trigger.new){
      ProjectTaskResourceIds.add(p.Resource_incurred_hours_Update__c);
    }
  }
 
  //When deleting Incurred Hours
  if(trigger.isDelete){
    for( Candidate_Task__c p : trigger.old){
      ProjectTaskResourceIds.add(p.Resource_incurred_hours_Update__c);
    }
  }
 
  //Map will contain one ProjectTaskResource Id to one sum value
  map<Id,Double> ProjectTaskResourceMap = new map<Id,Double> ();
 
  //Produce a sum of Resource_incurred_hours_Update__c and add them to the map
  //use group by to have a single ProjectTaskResource Id with a single Incurred Hours
  for(AggregateResult q : [select sum(Resource_incurred_hours_Update__c)
    from Candidate_Task__c where Candidate_Task__c IN :ProjectTaskResourceIds group by Candidate_Task__c]){
      ProjectTaskResourceMap.put((Id)q.get('Candidate_Task__c'),(Double)q.get('expr0'));
  }
 
  List ProjectTaskResourceToUpdate = new List();
 
  //Run the for loop on ProjectTaskResource using the non-duplicate set of ProjectTaskResource Ids
  //Get the sum value from the map and create a list of ProjectTaskResource to update
  for(Candidate_Task__c o : [Select Id, Resource_incurred_hours_Update__c from Candidate_Task__c where Id IN :ProjectTaskResourceIds]){
    Double IncurredHoursSum = ProjectTaskResourceMap.get(o.Id);
    o.Resource_incurred_hours_Update__c = IncurredHoursSum;
    ProjectTaskResourceToUpdate.add(o);
  }
 
  update ProjectTaskResourceToUpdate;
}
 
Anyone sort out my issue. Thanks in advance.
 
Thanks,
Manu
Best Answer chosen by Salesforce2015
Anupam RastogiAnupam Rastogi
Hi Manu,

Here is the modified simplier code that Rolls Up the 'Resource_incurred_hours_Update__c' field from all the child records to the parent Case field 'Total_Hours_Update__c'.

Just one thing to check before you use this code - 
   If you see I have used 'Double' for retrieving the value for Resource_incurred_hours_Update__c. If you get an error in your application because of it then try changing the Type to appropriate value like Integer as per the configuration done for fields Resource_incurred_hours_Update__c and Total_Hours_Update__c.
 
trigger RollUpIncurredHours on Candidate_Task__c (after delete, after insert, after update) {
    
    //--- To hold the list of cases that needs to be updated with the latest roll up values
    List <Case> caseList = new List<Case>();
    Candidate_Task__c[] objs = Trigger.isDelete ? Trigger.old : Trigger.new;
    Id accId = Null;
    
    for(Candidate_Task__c m : objs){
        
        if(Trigger.isInsert || Trigger.isDelete)
            accId = m.Case__c;
        else if(Trigger.isUpdate) {
            Candidate_Task__c mOld = Trigger.oldMap.get(m.Id);
            if((mOld.Case__c != Null) && (m.Case__c == Null))
                accId = mOld.Case__c;
            else if(m.Case__c != Null)
                accId = m.Case__c;                
        }
        
        if(accId != Null) {
            List<AggregateResult> groupResult = [select SUM(Resource_incurred_hours_Update__c) from Candidate_Task__c where Case__c <> Null and Case__c = :accId group by Case__c];
            Case a = [select Id, Total_Hours_Update__c from Case where Id = :accId];
            if(groupResult.size() > 0)
                a.Total_Hours_Update__c = (Double)groupResult[0].get('expr0');
            else
                a.Total_Hours_Update__c = 0;
            caseList.add(a);
        }
    }
    if(accId != Null)
    	update caseList;
}

Thanks
AR

If the reply resolves your problem then please mark it as best answer.

All Answers

Sai KethuSai Kethu
HI Manu

Try adding Adding id of the Candidate_Task__c record into the ProjectTaskResourceIds set instead of adding Resource_incurred_hours_Update__c field. So that your query will fetch appropriate records and the feild will be updated with correct value eventually.

Regards
Sai

P.S : If find the answer helpful and it resolves your issue. Please choose my answer as best answer.
Salesforce2015Salesforce2015
Hi Sai,

Thanks for quick reply.
I'm newbie, please correct my trigger and paste in comment.
Please advise me where (Object) I need to write this trigger.
Thanks in advance.
ManojjenaManojjena
Hi Manu ,

You have done a small mistake here 
for(Candidate_Task__c p : trigger.new){
    ProjectTaskResourceIds.add(p.Resource_incurred_hours_Update__c);  // Here you need to add the case lookup field 
}
Go to Candidate_Task__c  fileds check the look up field api name which create relationship with Case .\
Also you need to add undelete event in your trigger .

Please let me know if you need any help .Please post your lookup filed api name for further assistant .
Salesforce2015Salesforce2015
Hi Manoj,

Please find below infomation.

Parent Object   - Case
Child Object    - Candidate_Task__c
Child object custom number field    - Resource_incurred_hours_Update__c
Parent object custom number field  - Total_Hours_Update__c
Below is Lookup Field information on child object.
Field API Name - Case__c

Lookup Field

Thanks,
Manu
Anupam RastogiAnupam Rastogi
Hi Manu,

Here is the modified simplier code that Rolls Up the 'Resource_incurred_hours_Update__c' field from all the child records to the parent Case field 'Total_Hours_Update__c'.

Just one thing to check before you use this code - 
   If you see I have used 'Double' for retrieving the value for Resource_incurred_hours_Update__c. If you get an error in your application because of it then try changing the Type to appropriate value like Integer as per the configuration done for fields Resource_incurred_hours_Update__c and Total_Hours_Update__c.
 
trigger RollUpIncurredHours on Candidate_Task__c (after delete, after insert, after update) {
    
    //--- To hold the list of cases that needs to be updated with the latest roll up values
    List <Case> caseList = new List<Case>();
    Candidate_Task__c[] objs = Trigger.isDelete ? Trigger.old : Trigger.new;
    Id accId = Null;
    
    for(Candidate_Task__c m : objs){
        
        if(Trigger.isInsert || Trigger.isDelete)
            accId = m.Case__c;
        else if(Trigger.isUpdate) {
            Candidate_Task__c mOld = Trigger.oldMap.get(m.Id);
            if((mOld.Case__c != Null) && (m.Case__c == Null))
                accId = mOld.Case__c;
            else if(m.Case__c != Null)
                accId = m.Case__c;                
        }
        
        if(accId != Null) {
            List<AggregateResult> groupResult = [select SUM(Resource_incurred_hours_Update__c) from Candidate_Task__c where Case__c <> Null and Case__c = :accId group by Case__c];
            Case a = [select Id, Total_Hours_Update__c from Case where Id = :accId];
            if(groupResult.size() > 0)
                a.Total_Hours_Update__c = (Double)groupResult[0].get('expr0');
            else
                a.Total_Hours_Update__c = 0;
            caseList.add(a);
        }
    }
    if(accId != Null)
    	update caseList;
}

Thanks
AR

If the reply resolves your problem then please mark it as best answer.
This was selected as the best answer
Salesforce2015Salesforce2015
Hi AR,

Trigger saved sucessfully on object Candidate_Task__c.
But trigger not updating the total hours field on case object.
Anupam RastogiAnupam Rastogi
See, I tested it in my app after writing and it worked properly.

I had Account as Parent and Merchandise as child. Account's field was getting updated properly with the roll up value of a custom field present on Mercandise object.

I would suggest you to add couple of System.debug statements here and there, and check the outputs.

Thanks
AR
Anupam RastogiAnupam Rastogi
Did you check the line of code - 
    a.Total_Hours_Update__c = (Double)groupResult[0].get('expr0')

What is the type of field 'Total_Hours_Update__c' in your app?

And for adding System.debug statements, add them in every loop or If statement with some relevant value from that loop and see logs which all were executed and with what values.
Salesforce2015Salesforce2015
Can you please look into below trigger for same issue.


Trigger trigger on Candidate_Task__c(After Insert) {

Map<Id, Integer> mapCaseIdToHoursValue = new Map<Id, Integer>();
list<Case> lstCase = new list<Case>();

for(Candidate_Task__c objCT : trigger.new)
{
    if(objCT.Case__c != null && objCT.Resource_incurred_hours_Update__c != null)
    {
        if(mapCaseIdToHoursValue.containskey(objCT.Case__c))
            mapCaseIdToHoursValue.put(objCT.Case__c, mapCaseIdToHoursValue.get(objCT.Case__c) + objCT.Resource_incurred_hours_Update__c);
        else
            mapCaseIdToHoursValue.put(objCT.Case__c, objCT.Resource_incurred_hours_Update__c);
    }
}

for(Id objCaseId : mapCaseIdToHoursValue.keyset())
{
    Case objUpdatedCase = new Case(Id = objCaseId);
    objUpdatedCase.Total_Hours_Update__c = objUpdatedCase.Total_Hours_Update__c + mapCaseIdToHoursValue.get(objCaseId);
    lstCase.add(objUpdatedCase);
    
}

if(!lstCase.isEmpty())
    update lstCase;

}
Anupam RastogiAnupam Rastogi
Hi Manu,

Share the following information - 
            Data Type for fields Total_Hours_Update__c and Resource_incurred_hours_Update__c 

I have made a few changes to the code and added some debug statements. Copy paste this code and do some tests like - 

1. Create a new Case
2. Create few Candidate Task records with the Resource_incurred_hours_Update__c  field filled with some hours for each child record
3. Check logs and share it here. I want to see which all debug statements got executed on creation of new child records.
trigger RollUpIncurredHours on Candidate_Task__c (after delete, after insert, after update) {
    
    List <Case> caseList = new List<Case>();
    Candidate_Task__c[] objs = Trigger.isDelete ? Trigger.old : Trigger.new;
    Id accId = Null;
    
    for(Candidate_Task__c m : objs){
        
        if(Trigger.isInsert || Trigger.isDelete) {
            accId = m.Case__c;
            System.debug('I either Created or Deleted a Case record.');
        }
        else if(Trigger.isUpdate) {
            Candidate_Task__c mOld = Trigger.oldMap.get(m.Id);
            if((mOld.Case__c != Null) && (m.Case__c == Null))
                accId = mOld.Case__c;
            else if(m.Case__c != Null)
                accId = m.Case__c;     
            System.debug('I updated a Case record.');
        }
        
        if(accId != Null) {
            System.debug('I am almost there. Case Id = ' + accId);
            List<AggregateResult> groupResult = [select SUM(Resource_incurred_hours_Update__c) from Candidate_Task__c where Case__c <> Null and Case__c = :accId group by Case__c];
            Case a = [select Id, Total_Hours_Update__c from Case where Id = :accId];
            if(groupResult.size() > 0) {
                System.debug('I found the Case to update and its total hours = ' + (Double)groupResult[0].get('expr0'));
                a.Total_Hours_Update__c = (Double)groupResult[0].get('expr0');
                
            }
            else {
                System.debug('I found the Case to update and its total hours = 0.');
                a.Total_Hours_Update__c = 0;
            }
            caseList.add(a);
        }
    }
    if(caseList.size() > 0) {
        System.debug('And I am finally updating the Cases.');
    	update caseList;
        System.debug('Number of Cases updated = ' + caseList.size());
    }
}

Thanks
AR
 
Salesforce2015Salesforce2015
Hi AR,

Below are new case record and Debug logs.

New Case record

Debug Log

Thanks,
Manu
Anupam RastogiAnupam Rastogi
Hi Manu,

Is the problem resolved? The best answer does not identify that though ;)

The logs you have shared does not help much as I am not able to see the main content. I would suggest you to add the log content as a reply post. I am basically interested to see the System.debug statements that executed, in the logs.

Thanks
AR
Salesforce2015Salesforce2015
Hi AR,

Below is my requirement.

Case record with child records (Project-Task-Resources):

Case record with child records (Project-Task-Resources)

Objects and Fields:

Objects and Fields

Requirement:

Update Case standard object "Total Hours" field from sum of child object (Project-Task-Resources) records having field "Resource Incurred hours".


Thanks,
Manu.
 
Anupam RastogiAnupam Rastogi
Hi Manu,

Correct me if I am wrong, I see big difference in the configuration of these two fields and what you are expecting.

Not sure if you realize this, but I can see the two highlighted fields above as Formula fields. How are you then trying to update them using a Trigger?? Secondly, the fields API names you have used in the Trigger are different than the ones shown above.

Have you highlighed wrong fields above, and even placed wrong fields on the page layout or what, please clarify.

You had shared that the fields in picture are - 
Total_Hours_Update__c
Resource_incurred_hours_Update__c 

But the above screenshot shows the fields as, both of which are formula fields - 
Total_Hours__c
Resource_incurred_hours__c 

AR
 
Salesforce2015Salesforce2015
Hi AR,

you are correct.
my recent post was my actual requirement.
Those two are formula fields, so i created two number fields with same name. But difference in api name as _Update for new fields.
So i given new fields info for reference.
 Please use below new number fields for trigger.
Total_Hours_Update__c
Resource_incurred_hours_Update__c
Remaining all objects and fields are same.

Please let me know if you need further information.
 
Salesforce2015Salesforce2015
Hi AR,

Now i got it, i created number field as Resource_incurred_hours_Update__c.
First we need to update Resource_incurred_hours_Update__c number field with value, after then sum all fields Resource_incurred_hours_Update__c and populate in Total_Hours_Update__c number field.
 
Anupam RastogiAnupam Rastogi
Hi Manu,

I guess if the formula fields you created were to be used for the same pupose of holding the individual child record hours and the total rolled up hours then you should replace them with the number fields that you created with different API names.
And expose these fields on the page layout, as these fields are updated using the Trigger.

Then the roll up should properly work.

Thanks
AR

If you found my solution useful that solved your problem then mark it as best answer.
ManojjenaManojjena
Hi Manu ,
trigger RollUpIncurredHours on Candidate_Task__c ( after insert, after update,after delete,after undelete) {
	 Set<Id> caseIdSet=new Set<Id>();
	 List<Case> caseListToUpdate=new List<Case>();
	if(Trigger.isInsert || Trigger.isUpdate || Trigger.isUndelete){
		for(Candidate_Task__c cand : Trigger.new){
		    if(cand.Case__c != null)
				caseIdSet.add(cand.Case__c);     
        }
	}If(Trigger.isDelete){
	   for(Candidate_Task__c cand : Trigger.old){
			if(cand.Case__c != null)
				caseIdSet.add(cand.Case__c);     
        }
	}
   for(AggregateResult res : [SELECT Case__c,sum(Resource_incurred_hours_Update__c)can FROM Candidate_Task__c WHERE Case__c IN :caseIdSet group by Case__c]) {
	      caseListToUpdate.add(new Case(Id=(Id)res.get('Case__c'),Total_Hours_Update__c=(Double)res.get('can')));
	}
	try{
	  update caseListToUpdate;
	}catch(DmlException de){
	  System.debug(de);
	}
}

Please try with above code an dlet me knoe any issue ,I was bit busy in my work so i did not respond in time .
Salesforce2015Salesforce2015
Hi Manoj,

If class and trigger both fires rollup summary update.
What will hapen?
My scnario class only update, our trigger not fired. I copied your trigger on Candidate_Task__c and it not update the number field in case using trigger.
Please give me a suggestion.
Anupam RastogiAnupam Rastogi
Hi Manu, 

Did you try exposing the number fields on the UI? Because the number fields are getting used in the code therefore the final roll up value will also be shown in them. First check them.

Thanks
AR
Salesforce2015Salesforce2015
Hi Anupam,

Thanks for quick response.
I tried to copy my code here, but character issue. So, please give me a test mail on manoharsfdc401@gmail.com

Thanks,
Manu
Salesforce2015Salesforce2015
Hi Anupam,

I checked, field showing on pagelayout. But trigger will not update the child value.
From google search i find solution, I tried in class its working fine, but it not updating wrong value for second case record.

Please look below images.

1st Case record for Rest Project - working fine (40+20+50 = 110).

Case Record_1

2nd Case record for Rest Project - Updaing wrong value (1st case field value 110 + (20+30) = 160).

Case Record_2.

 
Salesforce2015Salesforce2015
Hi Anupam,

Thanks for the trigger.
I tested with your provided trigger with my object and fields modification like below in my Dev Org it working fine.

But, i have one more question.
As of now in our production org we have two triggers on same object Candidate_Task__c.
1. updateEmployeeField
2. HoursNoDeleteonCandidateTask

Current trigger was third trigger. Will three triggers working at a time (or) i need to club these three triggers as one trigger.


trigger RollUpIncurredHours on Candidate_Task__c (after delete, after insert, after update) {
    
    //--- To hold the list of cases that needs to be updated with the latest roll up values
    List <Case> CaseList = new List<Case>();
    Candidate_Task__c[] objs = Trigger.isDelete ? Trigger.old : Trigger.new;
    Id CaseId = Null;
    
    for(Candidate_Task__c m : objs){
        
        if(Trigger.isInsert || Trigger.isDelete)
            CaseId = m.Case__c;
        else if(Trigger.isUpdate) {
            Candidate_Task__c mOld = Trigger.oldMap.get(m.Id);
            if((mOld.Case__c != Null) && (m.Case__c == Null))
                CaseId = mOld.Test__c;
            else if(m.Case__c != Null)
                CaseId = m.Case__c;                
        }
        
        if(CaseId != Null) {
            List<AggregateResult> groupResult = [select SUM(Resource_incurred_hours__c) from Candidate_Task__c where Case__c <> Null and Case__c = :CaseId group by Case__c];
            Case c = [select Id, Total_Hours_new__c from Case where Id = :CaseId];
            if(groupResult.size() > 0)
                c.Total_Hours_new__c = (Double)groupResult[0].get('expr0');
            else
                c.Total_Hours_new__c = 0;
            CaseList.add(c);
        }
    }
    if(CaseId != Null)
        update CaseList;
}


Thanks,
Manu
Anupam RastogiAnupam Rastogi
Hi Manu,

Good to hear that your problem is resolved.

Regarding multiple triggers on the same object - 
1. Make sure that the functionalities implemented in these triggers do not contradict each other
2. As a best practise considering better code manageability, you should have one Trigger having all required events mentioned in it like before insert, after update etc. You can use the Trigger Context Variables to identify the different events for further processing. Here is the link to the same: https://www.salesforce.com/us/developer/docs/apexcode/Content/apex_triggers_context_variables.htm
But again it is personal choice to combine multiple triggers on same object into one, though I would do that.

Thanks
AR
Salesforce2015Salesforce2015
Hi Anupam,

I'm already given my third trigger code on object Candidate_Task__c.
Please find the below two triggers. Please help me to comebine these three triggers.


1. updateEmployeeField


trigger updateEmployeeField on Candidate_Task__c (before insert, before update) {
    System.debug('test');
    if(Trigger.isUpdate) {
        List<Id> resourceIds = new List<Id>();
         //Populate a list of all related cases
         for(Candidate_Task__c cs : trigger.new)
         {
              resourceIds.add(cs.Project_Resource__c);
         }        
         if(resourceIds.size()>0)
         {    
          //Query those resurces
              List<Project_Resource__c> resources = [Select Id, Candiadate_user__c from Project_Resource__c where Id in: resourceIds];                                                         
             //Create a map
              Map<Id,Project_Resource__c> resourceMap = new Map<Id,Project_Resource__c>(resources);
              //Update the Project_Resource__c objects
              for(Candidate_Task__c cs : trigger.new) {
                   //Only process if there's a case mapped
                   if(resourceMap.containsKey(cs.Project_Resource__c)) {
                        //Set the values
                        cs.Employee__c = resourceMap.get(cs.Project_Resource__c).Candiadate_user__c;               
                   }              
              }
         }
        
    }     
    if (trigger.isBefore && trigger.isInsert) {
        List<Id> resourceIds = new List<Id>();
         //Populate a list of all related cases
         for(Candidate_Task__c cs : trigger.new)
         {
              resourceIds.add(cs.Project_Resource__c);
         }        
         if(resourceIds.size()>0)
         {    
          //Query those resurces
              List<Project_Resource__c> resources = [Select Id, Candiadate_user__c from Project_Resource__c where Id in: resourceIds];                                                         
             //Create a map
              Map<Id,Project_Resource__c> resourceMap = new Map<Id,Project_Resource__c>(resources);
              //Update the Project_Resource__c objects
              for(Candidate_Task__c cs : trigger.new) {
                   //Only process if there's a case mapped
                   if(resourceMap.containsKey(cs.Project_Resource__c)) {
                        //Set the values
                        cs.Employee__c = resourceMap.get(cs.Project_Resource__c).Candiadate_user__c;               
                   }              
              }
         }       
        
    } 
               
}



2. HoursNoDeleteonCandidateTask


/*
 Raj Vooturi - display error for delete on Task
     
*/
trigger HoursNoDeleteonCandidateTask on Candidate_Task__c (before delete) {
    for (Candidate_Task__c a : Trigger.old) {
        if(trigger.isdelete) {
            if ( (a.Resource_incurred_hours__c>0) || (a.Resource_Incurred_Expense__c>0) ) {
                a.addError('You can\'t delete this Task Resource Record. This Task Resource has time and expense records that are submitted by resources');
            }
        }    
    }    
}


Thanks,
Manu
Salesforce2015Salesforce2015
Hi Anupam Rastogi,

Thank you for resolving my trigger issue.
Please see my below same question, but overlapping Apex Class and Apex trigger code.
Please look into below link.

https://developer.salesforce.com/forums/#!/feedtype=SINGLE_QUESTION_DETAIL&dc=Apex_Code_Development&criteria=OPENQUESTIONS&id=906F0000000BGvbIAG

Thanks,
Manu