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
NANCY1NANCY1 

How to update all child records??

Hi,

 

I have a master detail relationship between Opportunity( as master) and Custom object (as detail).

 

I want to update all the detail records, if i change / update any detail record. I mean all the detail records should contain same value. If i am changing any record, that should get changed other detail records as well..

 

trigger updateallRequisitions on Opportunity (after update) {
    List < Id > OppsIds = new List < Id >();
    List < Id > RRIds = new List < Id >();
    for(Opportunity rem: Trigger.New) {    
        if(rem.test__c != 'Closed'){    
           List<RR__c> proObj = [select p.Id,p.Bill_Rate__c from RR__c p where p.Opportunity__c =:rem.Id];
           if(proObj.size() > 0){
               for(RR__c pc: proObj ){
                   pc.Bill_Rate__c = rem.test__c ;
                   update pc;
               }
           }          
        }      
    }
}

 

 

 

Thanks

Best Answer chosen by Admin (Salesforce Developers) 
Jake GmerekJake Gmerek

I thought that the current validation would take care of that but I was wrong, sorry about that.  I will explicitly exclude the calling record and that should take care of the problem.  Here is the code:

 

}

trigger setValue on RR__c (before update)
{
  List<ID> OppIDs = new List<ID>();
  Boolean needsUpdate = FALSE; 
    for (RR__c RRs: trigger.new)
    {
    OppIDs.add(RRs.Opportunity__c);
    }
    List<RR__c> otherRR = new List<RR__c>([select id, Bill_Rate__c,Opportunity__c from RR__c where Opportunity__c in: OppIDs]);
    for (RR__c RRs: trigger.new)
    {
    for (integer i=1; i < otherRR.size(); i++)
     {
        if ((otherRR[i].Opportunity__c == RRs.Opportunity__c) && (otherRR[i].Bill_Rate__c != RRs.Bill_Rate__c)  && (otherRR[i].id != RRs.id))
        {
         otherRR[i].Bill_Rate__c = RRs.Bill_Rate__c;
         needsUpdate = TRUE;
        }
     }
    }
  if (needsUpdate){
    update otherRR;
  }

 

That is strange cause this logic otherRR[i].Bill_Rate__c != RRs.Bill_Rate__c should preclude the calling record from adding itself to the list, but the red check should resolve it.

All Answers

Jake GmerekJake Gmerek

Hello, the trigger that you have written is firing when the opprotunity is being Updated not when a RR__c record is being updated.  It sounds from your description that you want this to fire when the RR__c record is being edited.  That being said, below I have fixed your trigger to do what your trigger was trying to do:

trigger updateallRequisitions on Opportunity (after update) {
    List < Id > OppsIds = new List < Id >();
    for(Opportunity rem: Trigger.New) {
      if(rem.test__c != 'Closed'){
        OppIds.add(rem.id);
      }
    }
     List<RR__c> proObj = [select p.Id,p.Bill_Rate__c from RR__c p where p.Opportunity__c IN: OppsIds];       
        if(proObj.size() > 0){    
          for(RR__c pc: proObj ){
            pc.Bill_Rate__c = rem.test__c ;
          }
        }          
      Update proObj;              
}
}

 Notice I pulled all of your SOQL statements out of any for loops and we are only calling Update one time.  This is good practice to avioid hitting governor limits with these statements during bulk updates.

 

Finally, if you do need it the way that you described and not the way you wrote it, let me know and I will modify the trigger to reflect that.

 

Hope this helps.

Ritesh AswaneyRitesh Aswaney

Agree with Jake, just a minor optimisation suggestion

 

Given that its an after udpate trigger, trigger.newMap will be available, so you wouldn't need to iterate and aggregate your opportunity ids, you could use IN :trigger.NewMap directly.

 

 

NANCY1NANCY1

Hi Jake,

 

I should have explain the need in much specific way.. as it was creating ambiguity..

 

Yes, i want the trigger to update all RR__c records, if i update any RR__c record (with the updated value), i mean i want this to fire when any RR__c record is being edited..need is something that i have described...

 

Thanks

 

 

Jake GmerekJake Gmerek

OK so here is a trigger that should get you going:

 

trigger setValue on RR__c (before update){
  List<ID> OppIDs = new List<ID>();
  Boolean needsUpdate = FALSE;  //tests for parallel processes so we
                                //do not call update on the record 
                                //that called the trigger and get an 
                                //infinite loop error
  //get all the Opp IDs for bulk processing
  for (RR__c RRs: trigger.new){
    OppIDs.add(RRs.Opportunity__c);
  }
  List<RR__c> otherRR = new List<RR__c>([select id, Bill_Rate__c from RR__c where Opportunity__c in: OppIDs]);
  //Double for loop sort of sucks but we need to check each otherRR
  //for each record that is being updated 
  for (RR__c RRs: trigger.new){
    for (integer i; i < otherRR.size(); i++){
      //matches if the otherRR record is a sibling of the record being
      //updated, then checks to make sure the record has not already
      //been updated, avoiding an infinite loop
      if (RRs.Opportunity__c == other[i].Opportunity__c && other[i].Bill_Rate__c != RRs.Bill_Rate__c){
        other[i].Bill_Rate__c = RRs.Bill_Rate__c;
        needsUpdate = TRUE;
      }
    }
  }
  if (needsUpdate){
    update otherRR;
  }
}

 I have not tested the code above in a live environment, but I do something similar in my org and I modeled the trigger after one I already have so barring any syntax errors it should be OK.

 

Good Luck, I hope this works for you.

NANCY1NANCY1

Hi Jake,

 

I am getting the following error while implementing the below posted trigger: Please have a look..

 

"setValue: execution of BeforeUpdate

 

caused by: System.SObjectException: SObject row was retrieved via SOQL without querying the requested field: RR__c.Opportunity__c

 

Trigger.setValue: line 14, column 46"

 

trigger setValue on RR__c (before update)
{
  List<ID> OppIDs = new List<ID>();
  Boolean needsUpdate = FALSE; 
    for (RR__c RRs: trigger.new)
    {
    OppIDs.add(RRs.Opportunity__c);
    }
    List<RR__c> otherRR = new List<RR__c>([select id, Bill_Rate__c from RR__c where Opportunity__c in: OppIDs]);
    for (RR__c RRs: trigger.new)
    {
    for (integer i=0; i <= otherRR.size(); i++)
     {
        if (RRs.Opportunity__c == otherRR[i].Opportunity__c || otherRR[i].Bill_Rate__c != RRs.Bill_Rate__c) <line 14>
        {
         otherRR[i].Bill_Rate__c = RRs.Bill_Rate__c;
         needsUpdate = TRUE;
        }
     }
    }
  if (needsUpdate){
    update otherRR;
  }
}

Jake GmerekJake Gmerek

All you have to do is add opportunity__c to your query.

 

}

trigger setValue on RR__c (before update)
{
  List<ID> OppIDs = new List<ID>();
  Boolean needsUpdate = FALSE; 
    for (RR__c RRs: trigger.new)
    {
    OppIDs.add(RRs.Opportunity__c);
    }
    List<RR__c> otherRR = new List<RR__c>([select id, Bill_Rate__c, Opportunity__c from RR__c where Opportunity__c in: OppIDs]);
    for (RR__c RRs: trigger.new)
    {
    for (integer i=0; i <= otherRR.size(); i++)
     {
        if (RRs.Opportunity__c == otherRR[i].Opportunity__c || otherRR[i].Bill_Rate__c != RRs.Bill_Rate__c) <line 14>
        {
         otherRR[i].Bill_Rate__c = RRs.Bill_Rate__c;
         needsUpdate = TRUE;
        }
     }
    }
  if (needsUpdate){
    update otherRR;
  }

NANCY1NANCY1

Hi,

 

I am getting the following error while editing the record...

 

setValue: execution of BeforeUpdate

caused by: System.ListException: List index out of bounds: 4

Trigger.setValue: line 14, column 22

 

 Note <where 4, is the total number of records for that opportunity)

Jake GmerekJake Gmerek

It is because in your for loop you have <= instead of <, id did not notice it last time cause I just looked at the line you underlined. This line:

 

for (integer i=0; i <= otherRR.size(); i++)

 

needs to become:

 

for (integer i=0; i < otherRR.size(); i++)

 

This is because arrays start at 0 not 1 so the for loop as you have it written is looping 5 times not 4, so essentially otherRR has four elements otherRR[0], otherRR[1], otherRR[2], otherRR[3], but you code is trying to access otherRR[4] which does not work.

 

 

NANCY1NANCY1

Hi Jake, I tried the same.. but was getting the following error:

 

Error: Invalid Data. Review all error messages below to correct your data. Apex trigger setValue caused an unexpected exception, contact your administrator: setValue: execution of BeforeUpdate caused by: System.DmlException: Update failed. First exception on row 0 with id a0IL00000009T1kMAE; first error: SELF_REFERENCE_FROM_TRIGGER, Object (id = a0IL00000009T1k) is currently in trigger setValue, therefore it cannot recursively update itself: []: Trigger.setValue: line 22, column 5

 

trigger setValue on RR__c (before update)
{
  List<ID> OppIDs = new List<ID>();
  Boolean needsUpdate = FALSE; 
    for (RR__c RRs: trigger.new)
    {
    OppIDs.add(RRs.Opportunity__c);
    }
    List<RR__c> otherRR = new List<RR__c>([select id, Bill_Rate__c,Opportunity__c from RR__c where Opportunity__c in: OppIDs]);
    for (RR__c RRs: trigger.new)
    {
    for (integer i=1; i < otherRR.size(); i++)
     {
        if ((otherRR[i].Opportunity__c == RRs.Opportunity__c) && (otherRR[i].Bill_Rate__c != RRs.Bill_Rate__c))
        {
         otherRR[i].Bill_Rate__c = RRs.Bill_Rate__c;
         needsUpdate = TRUE;
        }
     }
    }
  if (needsUpdate){
    update otherRR;
  }
}

Jake GmerekJake Gmerek

I thought that the current validation would take care of that but I was wrong, sorry about that.  I will explicitly exclude the calling record and that should take care of the problem.  Here is the code:

 

}

trigger setValue on RR__c (before update)
{
  List<ID> OppIDs = new List<ID>();
  Boolean needsUpdate = FALSE; 
    for (RR__c RRs: trigger.new)
    {
    OppIDs.add(RRs.Opportunity__c);
    }
    List<RR__c> otherRR = new List<RR__c>([select id, Bill_Rate__c,Opportunity__c from RR__c where Opportunity__c in: OppIDs]);
    for (RR__c RRs: trigger.new)
    {
    for (integer i=1; i < otherRR.size(); i++)
     {
        if ((otherRR[i].Opportunity__c == RRs.Opportunity__c) && (otherRR[i].Bill_Rate__c != RRs.Bill_Rate__c)  && (otherRR[i].id != RRs.id))
        {
         otherRR[i].Bill_Rate__c = RRs.Bill_Rate__c;
         needsUpdate = TRUE;
        }
     }
    }
  if (needsUpdate){
    update otherRR;
  }

 

That is strange cause this logic otherRR[i].Bill_Rate__c != RRs.Bill_Rate__c should preclude the calling record from adding itself to the list, but the red check should resolve it.

This was selected as the best answer
NANCY1NANCY1

Hi Jake,

 

This time i didn't get any error... :) but my other records are not getting updated...

 

i mean.. lets say i have 4 records for an opportunity..and i update the value for field Bill_Rate__c for one record.. then it should get copied (i.e. update) other records as well, but that is not happening....

 

 

Jake GmerekJake Gmerek

That is my fault this needs to be an after update trigger so your first line should be

 

trigger setValue on RR__c (after update)

 

you can also add a comma after after update and add after insert if you want the trigger to run on new records as well.

NANCY1NANCY1

Hi Jake,

 

Many Thanks for taking this forward and finally resolve it. I am marking the solution accepted.. so that other people can get benefit out of it....thanks

 

Jake GmerekJake Gmerek

Your welcome

NANCY1NANCY1

Hi jake,

 

this should be my last query on this..i believe...

 

Currently all my records gets updated.. based on the changes made out in one particular record..earlier.. it was for a single field (Bill_Rate__c)..now i need the same thing should get happen on other 10 selected fields also...I mean whenever any changes will be made in those 10 fields.. it will get replicated in all the records...i changed the code and achieved the same...

 

But, there is one trouble doing that...lets say i have created two records one by one with different field values all together... and now, if i make any change in one record it gets replicated in another record...because both the records exists for a single opportunity...

 

to identify that these two records are different..we have a standard field.. CREATEDDATE.. which is different in both the records...now, if i try to update the records using this field.. i get the following error:

 

UpdateRequisitions: System.LimitException: Too many script statements: 200001

 

 

 

My current Code:

 

trigger UpdateRequisitions on Requisition__c (after update)

  List<ID> OppIDs = new List<ID>();
  Boolean needsUpdate = FALSE;
    for (Requisition__c RRs: trigger.new)
    {
    OppIDs.add(RRs.Opportunity__c);
    }
    List<Requisition__c> otherRR = new List<Requisition__c>([select id,CREATEDDATE,Priority__c,RR_Status__c,RM_Phase__c,Role__c,Long_Term_Resource_Location__c,New_Position__c,Required_Skill__c,Years_of_Experience__c,Opportunity__c from Requisition__c where Opportunity__c in: OppIDs]);
    for (Requisition__c RRs: trigger.new)
    {
    for (integer i=0; i < otherRR.size(); i++)
      {
        if ((otherRR[i].Opportunity__c == RRs.Opportunity__c) && (otherRR[i].id != RRs.id)
                && (otherRR[i].CREATEDDATE == RRs.CREATEDDATE)
                && ((otherRR[i].Priority__c != RRs.Priority__c)
                || (otherRR[i].RR_Status__c != RRs.RR_Status__c)
                || (otherRR[i].RM_Phase__c != RRs.RM_Phase__c)
                || (otherRR[i].Role__c != RRs.Role__c)
                || (otherRR[i].Long_Term_Resource_Location__c != RRs.Long_Term_Resource_Location__c)
                || (otherRR[i].New_Position__c != RRs.New_Position__c)
                || (otherRR[i].Required_Skill__c != RRs.Required_Skill__c)
                || (otherRR[i].Years_of_Experience__c != RRs.Years_of_Experience__c)))
               
        {
         otherRR[i].Bill_Rate__c = RRs.Bill_Rate__c;
         otherRR[i].Priority__c = RRs.Priority__c;
         otherRR[i].RR_Status__c = RRs.RR_Status__c;
         otherRR[i].RM_Phase__c = RRs.RM_Phase__c;
         otherRR[i].Role__c = RRs.Role__c;
         otherRR[i].Long_Term_Resource_Location__c = RRs.Long_Term_Resource_Location__c;
         otherRR[i].New_Position__c = RRs.New_Position__c;
         otherRR[i].Required_Skill__c = RRs.Required_Skill__c;
         otherRR[i].Years_of_Experience__c = RRs.Years_of_Experience__c;

         needsUpdate = TRUE;
        }
      }
    }
  if (needsUpdate)
  {
    update otherRR;
  }


}

 

 

 

 

Jake GmerekJake Gmerek

Nancy, this problem is much more difficult to debug than the others.  You are running up against APEX Governor limits.  For more information you can read about them here:

 

http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_gov_limits.htm

 

If you look you can see that you can only execute 200000 code statements and you are going over that.  The question is why.  There are several different things that you can look at to figure it out.

 

First thing to think about is:  Is this a manual update (from the Salesforce UI) or are you using the Dataloader or API?  From what we have talked about I believe that this is a manual update, but it never hurts to be sure.  This matters because Salesforce does batch processing when the dataloader and/or the API are used and that could have pushed you over the governor limits.

 

Next, consider the number of records being updated. Essentially consider how many rows the query,    List<Requisition__c> otherRR = ...; is returning.  You can play with that by adding a LIMIT statment to the very end of the query before the ).  Essentially start with LIMIT 1 and see if it runs on some test data and increase the LIMIT from there to see what the threshold is.  Once you figure that out you can make some decisions from there.  You can also go back to the last time that you had the code working and add changes a piece at a time so that you can see exactly what breaks the code.  Then you can use the following to figure out why it is breaking the code.

 

If neither of those leads to the cause of the problem the last place to look is the debug log.  First you have to set up monitoring.  Goto Setup->Monitoring->Debug Logs and set up yourself as a monitored user.  Then recreate the error (ie Update a record that causes the error. Then go back up to Your Name->System Logs.  Make sure you are using the new System Log and then clear the "This Session Only" check box.  You should see the log for your execution there and be able to look through it to see what is going on as the Trigger runs.

 

My feeling is that you will find your problem in the Debug Log.  It is possible that you are somehow ending up in an infinite loop and that is why you are hitting the Governor Limit.  Essentially when you call Update otherRR, each of those records will trigger the Trigger seperately, but they run under the same Governor Limits as the original trigger.  So essentially a record could be getting updated, update another record and that record could be updating the original record again resulting in an infinite loop.  The Debug log will show you if this is happening.  

 

Some tips on the debug log

1.  You can look at the debug log for a successful execution as well.  So you can roll back your trigger to some code that you know works and look at the log for that and see what the differences are between and valid and invalid execution.

2.  You can use system.debug('Some message here'); in your code to break up the debug log and easily find what is executing here.  You can even do something like system.debug('message in my for loop i=' + i) to see which iteration of the for loop you are on.

3.  For more info on the System Log you can look here:

 http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_debugging_system_log_console.htm

 

I hope this helps you find your problem.  If you have any questions let me know and I will do my best to answer them.

 

Jake GmerekJake Gmerek

Also I was looking at your code one last time to see if I can figure out where the infinite loop (assuming that it is a loop that is your problem) is coming from and I was wondering in your if statement you have this:

 

(otherRR[i].CREATEDDATE == RRs.CREATEDDATE)

 

from your description you seem to want this:

 

(otherRR[i].CREATEDDATE != RRs.CREATEDDATE)

 

because you want records where the CREATEDDATE is different not the same.