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
ArrowArrow 

Trigger to sync child opportunities with parent

New to triggers and trying to find a code example to do the following:

 

Child opportunities linked to parent opportunites on Parent_Opportunity__c lookup

 

If parent CloseDate or StageName changes, I would like a trigger to update all child opportuntiies

Best Answer chosen by Admin (Salesforce Developers) 
magicforce9magicforce9

Hi,

 

There are two things to consider when doing this in trigger, since you are will be updating Other Opportunity Records in the Opportunity trigger and this will cause recursion of trigger..So follow these steps.

 

1. First create a class with static variable as below

public with sharing class DisableOpportunityTriggerReRun {

	 public static boolean firstRun = True; 
	 
}

 2. Now you can use the sample code for your opportunity trigger or you can tweak it.

trigger UpdateChildOpportunityTrigger on Opportunity (after update) {
	
	if(DisableOpportunityTriggerReRun.firstRun){
		Set<Id> parentOpportunities = new set<id>();
		for(Opportunity opp : Trigger.new){

			if((opp.CloseDate != Trigger.oldMap.get(opp.id).CloseDate) ||  (opp.StageName != Trigger.oldMap.get(opp.id).StageName))
			parentOpportunities.add(opp.id);

		}

		List<Opportunity> childOpportunities = [Select Id, CloseDate, StageName, Parent_Opportunity__c from Opportunity where Parent_Opportunity__c IN :parentOpportunities];

		for(Opportunity opp : childOpportunities)
		{

			Date newCloseDate = Trigger.newMap.get(opp.Parent_Opportunity__c).CloseDate;
			String newStageName = Trigger.newMap.get(opp.Parent_Opportunity__c).StageName

			//Checking if the Parent Opportunity CloseDate is changed
			if(newCloseDate != Trigger.oldMap.get(opp.Parent_Opportunity__c).CloseDate)
			opp.CloseDate = newCloseDate;

			//Checking if the Parent Opportunity StageName is changed
			if(newStageName != Trigger.oldMap.get(opp.Parent_Opportunity__c).StageName)
			opp.StageName = newStageName;

		}
		DisableOpportunityTriggerReRun.firstRun = False;
		update childOpportunities;
	}
}

 Here is the test calss if you need.....Please be advised that I'm only assuming the StageName values in test class and you need to modify them if its different from what I've used and You'll also need to populate any required fields or fields required by validation rules for test to Pass.

@isTest
public with sharing class TestUpdateChildOpportunityTrigger {
	
	static testMethod void testCloseDateAndStageNameUpdate() {

			Opportunity parentOpt = new Opportunity(Name = 'Parent Opt', CloseDate = Date.Today(), StageName = 'Prospecting');
			insert parentOpt;
			Opportunity childOpt = new Opportunity(Name = 'Child Opt', CloseDate = Date.Today(), StageName = 'Prospecting', Parent_Opportunity__c = parentOpt.id);
			insert childOpt;

			parentOpt.CloseDate = Date.Today().addDays(1);
			parentOpt.StageName = 'Qualification';
			update parentOpt;
			Opportunity opt = [Select CloseDate, StageName from Opportunity where Id = :childOpt];
			system.assertEquals(opt.CloseDate, parentOpt.CloseDate);
			system.assertEquals(opt.StageName, parentOpt.StageName);
			}
	}

 

 

 

 

All Answers

magicforce9magicforce9

Hi,

 

There are two things to consider when doing this in trigger, since you are will be updating Other Opportunity Records in the Opportunity trigger and this will cause recursion of trigger..So follow these steps.

 

1. First create a class with static variable as below

public with sharing class DisableOpportunityTriggerReRun {

	 public static boolean firstRun = True; 
	 
}

 2. Now you can use the sample code for your opportunity trigger or you can tweak it.

trigger UpdateChildOpportunityTrigger on Opportunity (after update) {
	
	if(DisableOpportunityTriggerReRun.firstRun){
		Set<Id> parentOpportunities = new set<id>();
		for(Opportunity opp : Trigger.new){

			if((opp.CloseDate != Trigger.oldMap.get(opp.id).CloseDate) ||  (opp.StageName != Trigger.oldMap.get(opp.id).StageName))
			parentOpportunities.add(opp.id);

		}

		List<Opportunity> childOpportunities = [Select Id, CloseDate, StageName, Parent_Opportunity__c from Opportunity where Parent_Opportunity__c IN :parentOpportunities];

		for(Opportunity opp : childOpportunities)
		{

			Date newCloseDate = Trigger.newMap.get(opp.Parent_Opportunity__c).CloseDate;
			String newStageName = Trigger.newMap.get(opp.Parent_Opportunity__c).StageName

			//Checking if the Parent Opportunity CloseDate is changed
			if(newCloseDate != Trigger.oldMap.get(opp.Parent_Opportunity__c).CloseDate)
			opp.CloseDate = newCloseDate;

			//Checking if the Parent Opportunity StageName is changed
			if(newStageName != Trigger.oldMap.get(opp.Parent_Opportunity__c).StageName)
			opp.StageName = newStageName;

		}
		DisableOpportunityTriggerReRun.firstRun = False;
		update childOpportunities;
	}
}

 Here is the test calss if you need.....Please be advised that I'm only assuming the StageName values in test class and you need to modify them if its different from what I've used and You'll also need to populate any required fields or fields required by validation rules for test to Pass.

@isTest
public with sharing class TestUpdateChildOpportunityTrigger {
	
	static testMethod void testCloseDateAndStageNameUpdate() {

			Opportunity parentOpt = new Opportunity(Name = 'Parent Opt', CloseDate = Date.Today(), StageName = 'Prospecting');
			insert parentOpt;
			Opportunity childOpt = new Opportunity(Name = 'Child Opt', CloseDate = Date.Today(), StageName = 'Prospecting', Parent_Opportunity__c = parentOpt.id);
			insert childOpt;

			parentOpt.CloseDate = Date.Today().addDays(1);
			parentOpt.StageName = 'Qualification';
			update parentOpt;
			Opportunity opt = [Select CloseDate, StageName from Opportunity where Id = :childOpt];
			system.assertEquals(opt.CloseDate, parentOpt.CloseDate);
			system.assertEquals(opt.StageName, parentOpt.StageName);
			}
	}

 

 

 

 

This was selected as the best answer
ArrowArrow

Many thanks for your help, I did get an error which I fixed by adding a semi colon to the end of this line:

 

String newStageName = Trigger.newMap.get(opp.Parent_Opportunity__c).StageName

 Also wondering how I could change this code so that it also worked if there were grandchild opportunities or even great grandchildren.

 

Do you think that is possible to do?

 

Once again, many thanks for providing such a complete solution.