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
Jeff GJeff G 

Cross-Object Update Problems -- Probability field won't change

Hello,

I am trying to write an apex trigger that updates the Probability field of my Opportunity object whenever I change the probability field of a custom object 'QQ'. My Opportunity and QQ have a master-detail relationship.
  
I am new to apex triggers, though intuitively I would write something like this:

trigger TestUpdateProbability on Opportunity (before insert, before update) {
        for(Opportunity p : Trigger.new) {
                p.Probability = p.QQ__r.Total__c;
        }
}

This doesn't seem to be working.
I can't use a workflow either, because the Probability field in an Opportunity object is set according to the Stage field. 
I would like to change the Opportunity probability based on my custom QQ's probability, though that is difficult since each stage is associated with only one preset Probability percentage.

Does anyone know how to get around this? Thanks.
Best Answer chosen by Jeff G
Balaji Chowdary GarapatiBalaji Chowdary Garapati
@Jeff.,

 Hello Jeff, 

Important things to consider before we start.,

 -> Writing trigger on QQ object , while inserting ideally we wouldnt have any opportunities that were child to this QQ record so we doesnt need to handle any insert event
 -> We should query the opportunity records that were children to the QQ record that was being updated.
 -> Try the below link which explains the trigger context variables.
 https://www.salesforce.com/us/developer/docs/apexcode/Content/apex_triggers_context_variables.htm
 
 -> Trigger context variables will have the information of the records on which the trigger got fired.
 -> Ideally if the update is on cross object (any other objects record other than the trigger got fired), always go for the after trigger events
 -> Naming convention will play a major role in future, if we visit back the same code., so try to name the variables appropriatelyTotal__c

 Coming to the code., the trigger will be something like this:
 
trigger UpdateOppProb on QQ__c (after update) {
	
	Map<Id,Decimal>QQRecordMap=new Map<Id,Decimal>();//To capture the current QQ records that had any  change in total
	for(QQ__c QQRecord : Trigger.new) {
        // Verifying all possible cases of total changes that could happen than earlier
		if((trigger.oldMap.get(QQRecord.id).Total__c!=Null && QQRecord.Total__c!=Null &&trigger.oldMap.get(QQRecord.id).Total__c!=QQRecord.Total__c) || (trigger.oldMap.get(QQRecord.id).Total__c==Null && QQRecord.Total__c!=Null) || (trigger.oldMap.get(QQRecord.id).Total__c!=Null && QQRecord.Total__c==Null)){ 
			QQRecordMap.put(QQRecord.id,QQRecord.Total__c);
		}
    }//By end of this loop, the map will have all the QQ records info that we need
	
    //Now its time to query the opportunity records that need The change in probability
	
	List<Opportunity>OppsToUpdate=new List<Opportunity>();
	
	for(Opportunity OppRecord:[select id,QQ__c from Opportunity where QQ__c IN : QQRecordMap.keySet()]){
		OppsToUpdate.add(new Opportunity(id=OppRecord.id,QQRecordMap.get(OppRecord.QQ__c)));
	}// Now we prepared the list of opps that needs to get updated.
	
	if(OppsToUpdate!=Null && OppsToUpdate.size()>0){
		update OppsToUpdate;
	}
}



In the preivous trigger we queried the QQ information, where as in the current one we have the QQ information in our hand, we need to query all the opportunities that were tied to this guy as children and update them.



A small change in previous trigger,
trigger TestUpdateProbability on Opportunity (before insert, before update) {
		
		Set<Id>QQIdset=new Set<Id>();//to collect the qq ids that we need
		
        for(Opportunity p : Trigger.new) {
                
				QQIdset.add(p.QQ__c);
        }
		Map<Id,QQ__c>QQRecordMap=new Map<Id,QQ__C>([select id,Total__c from QQ__C where id in :QQIdset]);// Now a map with desired QQ records is ready
		//Loop through the opportunities to assign the probability by fetching it from the map based on QQ__C field
		
		
		for(Opportunity p : Trigger.new) {
                
				 p.Probability = QQRecordMap.get(p.QQ__c).Total__c;
        }
}

Note: Just try to change the variable names in the tirrger to meaningful names which will make more sense if we look back in future :)

Cheers.,


Hope it helps.,




If it did resolve your question/req, please mark it as solved, so that it might guide others!

Thanks,
Balaji

All Answers

SonamSonam (Salesforce Developers) 
You will have to first fetch the record from object QQ and then update its fields..something similar to the code shown in the  sample below:
http://developer.force.com/cookbook/recipe/mass-updating-contacts-when-an-account-changes  
Balaji Chowdary GarapatiBalaji Chowdary Garapati
@Jeffrey Gu

The relationship will not work in case of before trigger events, instead you need to query for it. If you are trying to get the total from QQ object(API NAME QQ__c) then try the below code:
 
trigger TestUpdateProbability on Opportunity (before insert, before update) {
		
		Set<Id>QQIdset=new Set<Id>();//to collect the qq ids that we need
		
        for(Opportunity p : Trigger.new) {
                
				QQIdset.add(QQ__c);
        }
		Map<Id,QQ__c>QQRecordMap=new Map<Id,QQ__C>([select id,Total__c from QQ__C where id in :QQIdset]);// Now a map with desired QQ records is ready
		//Loop through the opportunities to assign the probability by fetching it from the map based on QQ__C field
		
		
		for(Opportunity p : Trigger.new) {
                
				 p.Probability = QQRecordMap.get(p.QQ__c).Total__c;
        }
}

Hope it helps:

Thanks,
balaji
Jeff GJeff G
@Balaji Chowdary Garapati

Thanks for the help!

Your code did work, but I just realized that I want the Opportunity probability to automatically update as soon as I save my QQ probability. The trigger only fires when I edit and save on my Opportunity object.

Is it possible to repurpose the code as a trigger on QQ instead? I've been trying to do so, but I get an "Invalid foreign key relationship: QQ__c.Opportunity" whenever I try to access Opportunities.

Thanks again,
Jeff

 
Balaji Chowdary GarapatiBalaji Chowdary Garapati
@Jeff G:

 The code used to update opportunities based on Parents Total wont be much help in doing the reverse way.

You definitely need to have a trigger on QQ object which updates the probability on its childs when ever it gets updated.

And one more thing : 
Have you considered to use a formula field instead of input field which reflects the probability from parent? so you doesnt need to worry about the code?

 
Jeff GJeff G
@Balaji Chowdary Garapati

I have considered a formula field, but I would definitely prefer it if I could directly update the standard probability field of the opportunity through a change in my QQ total.

Is there a reason why this would not work? It's mainly your code, but in reverse:

trigger UpdateOppProb on QQ__c (before insert, before update) {
    Set<Id>OppIdSet = new Set<Id>();
    
    for(QQ__c q : Trigger.new) {
        OppIdSet.add(q.Opportunity__c);
    }
    
    Map<Id, Opportunity> OppRecordMap = new Map<Id, Opportunity>([
        Select id, Probability
            from Opportunity
                where id in : OppIdSet
    ]);
        
    for(QQ__c q : Trigger.new) {
        OppRecordMap.get(q.Opportunity__c).Probability = q.Total__c;
    }

}
Balaji Chowdary GarapatiBalaji Chowdary Garapati
@Jeff.,

 Hello Jeff, 

Important things to consider before we start.,

 -> Writing trigger on QQ object , while inserting ideally we wouldnt have any opportunities that were child to this QQ record so we doesnt need to handle any insert event
 -> We should query the opportunity records that were children to the QQ record that was being updated.
 -> Try the below link which explains the trigger context variables.
 https://www.salesforce.com/us/developer/docs/apexcode/Content/apex_triggers_context_variables.htm
 
 -> Trigger context variables will have the information of the records on which the trigger got fired.
 -> Ideally if the update is on cross object (any other objects record other than the trigger got fired), always go for the after trigger events
 -> Naming convention will play a major role in future, if we visit back the same code., so try to name the variables appropriatelyTotal__c

 Coming to the code., the trigger will be something like this:
 
trigger UpdateOppProb on QQ__c (after update) {
	
	Map<Id,Decimal>QQRecordMap=new Map<Id,Decimal>();//To capture the current QQ records that had any  change in total
	for(QQ__c QQRecord : Trigger.new) {
        // Verifying all possible cases of total changes that could happen than earlier
		if((trigger.oldMap.get(QQRecord.id).Total__c!=Null && QQRecord.Total__c!=Null &&trigger.oldMap.get(QQRecord.id).Total__c!=QQRecord.Total__c) || (trigger.oldMap.get(QQRecord.id).Total__c==Null && QQRecord.Total__c!=Null) || (trigger.oldMap.get(QQRecord.id).Total__c!=Null && QQRecord.Total__c==Null)){ 
			QQRecordMap.put(QQRecord.id,QQRecord.Total__c);
		}
    }//By end of this loop, the map will have all the QQ records info that we need
	
    //Now its time to query the opportunity records that need The change in probability
	
	List<Opportunity>OppsToUpdate=new List<Opportunity>();
	
	for(Opportunity OppRecord:[select id,QQ__c from Opportunity where QQ__c IN : QQRecordMap.keySet()]){
		OppsToUpdate.add(new Opportunity(id=OppRecord.id,QQRecordMap.get(OppRecord.QQ__c)));
	}// Now we prepared the list of opps that needs to get updated.
	
	if(OppsToUpdate!=Null && OppsToUpdate.size()>0){
		update OppsToUpdate;
	}
}



In the preivous trigger we queried the QQ information, where as in the current one we have the QQ information in our hand, we need to query all the opportunities that were tied to this guy as children and update them.



A small change in previous trigger,
trigger TestUpdateProbability on Opportunity (before insert, before update) {
		
		Set<Id>QQIdset=new Set<Id>();//to collect the qq ids that we need
		
        for(Opportunity p : Trigger.new) {
                
				QQIdset.add(p.QQ__c);
        }
		Map<Id,QQ__c>QQRecordMap=new Map<Id,QQ__C>([select id,Total__c from QQ__C where id in :QQIdset]);// Now a map with desired QQ records is ready
		//Loop through the opportunities to assign the probability by fetching it from the map based on QQ__C field
		
		
		for(Opportunity p : Trigger.new) {
                
				 p.Probability = QQRecordMap.get(p.QQ__c).Total__c;
        }
}

Note: Just try to change the variable names in the tirrger to meaningful names which will make more sense if we look back in future :)

Cheers.,


Hope it helps.,




If it did resolve your question/req, please mark it as solved, so that it might guide others!

Thanks,
Balaji
This was selected as the best answer
Jeff GJeff G
@Balaji

Sorry for the late reply, but that looks great! Unfortunately my problem is not totally resolved though...

There seems to be a compiling error at line 16 of your code:
OppsToUpdate.add(new Opportunity(id=OppRecord.id,QQRecordMap.get(OppRecord.QQ__c)));

It keeps returning an "expecting an equals sign, found '('" error. I believe this is because the QQ parameter of the new Opportunity to be added does not have a field initialiser. But if I try this:

QQ__c = QQRecordMapOppsToUpdate.add(new Opportunity(id=OppRecord.id, QQ__c = QQRecordMap.get(OppRecord.QQ__c)));

I just get an invalid initial expression type error, because it is expecting "Id" for some reason. Any thoughts?

You have been very helpful so far. I really appreciate your input!

Thanks,
Jeff

 
Balaji Chowdary GarapatiBalaji Chowdary Garapati
Oops.,

 Change  the below statement in UpdateOppProb  trigger code

 OppsToUpdate.add(new Opportunity(id=OppRecord.id,QQRecordMap.get(OppRecord.QQ__c)));

To:
 OppsToUpdate.add(new Opportunity(id=OppRecord.id,Total__c=QQRecordMap.get(OppRecord.QQ__c)));

For got to specifiy to which field we are assigning the calculated total.,


The statement does this:

  -> Collecting the opportunities we need to update, ids (to identify the opportunity) and total field(the total that should be in the total__c field of opportunity)


Cheers.,

Thanks,
Balaji