I was browsing the Dreamforce App last week and came across a question in the ChallengeUs group from Maria Belli (@msbelli on twitter) :
A few responses had already indicated she would need a trigger and I offered to assist if necessary. I worked with Maria and whipped up a trigger that suited her needs. Since this is functionality that could be applicable to any org I felt it would be worth sharing with my (small) audience.
One item to pay attention to that this trigger only works for certain record types. I am leaving this in as it is fairly easy to remove the code for this and also provides a good case to learn from.
In this trigger we are looking to set the ServiceDate on the Opportunty Products to the CloseDate of the Opportunity for certain record types when the CloseDate changes.
The first thing we do is get the record type Ids and add them to a Set. Next we loop through the opportunities in trigger.new and add any with a match to the record types in question to another Set. We are also checking to see whether the CloseDate has changed before adding the Opportunity to the Set.
Once you have establish the opportunities that match our record types you can query for those opportunities and their associated Products, which will be a list. You can then loop through the Opportunities and process each list of Products tied to it, setting the Product Service Date to the Opportunity Close Date.
The Trigger:
trigger Opportunity_CloseDate_Update on Opportunity (after update) {
//get the Ids of our two record types and add them to a set
Set<Id> recTypeIds = new Set<Id>();
for(RecordType r : [select Id, Name from RecordType where (Name='StandAlone Opportunity' OR Name='Subsidiary Opportunity') AND SobjectType='Opportunity']){
recTypeIds.add(r.id);
}
//check the opprtunites in the trigger to see if the Close Date change AND any are of the RecordTypes above.
//if so add them to a Set
Set<Id> StandAloneAndSubsidiaryOppIds = new Set<Id>();
for(Integer i=0;i<trigger.new.size();i++){
if(trigger.new[i].CloseDate <> trigger.old[i].CloseDate && recTypeIds.contains(trigger.new[i].RecordTypeId)){
StandAloneAndSubsidiaryOppIds.add(Trigger.new[i].id);
}
}
//Process any opps that have changed
if(StandAloneAndSubsidiaryOppIds.size() > 0){
//get all of the opportunity Line items tied to opportunities in this trigger
List<Opportunity> oliList = [select Id, Name, CloseDate, (select Id, ServiceDate, OpportunityId from OpportunityLineItems) from Opportunity where Id IN :StandAloneAndSubsidiaryOppIds];
//create list to hold opportunity line items that need updating.
List<OpportunityLineItem> oliUpdateList = new List<OpportunityLineItem>();
//loop through opportunites and update all of the opportunity line items. add line items to udpat list.
for(Opportunity o : oliList){
for(OpportunityLineItem oli : o.OpportunityLineItems){
oli.ServiceDate = o.CloseDate;
oliUpdateList.add(oli);
}
}
//update the line items
if(!oliUpdateList.isEmpty()){
update oliUpdateList;
}
}
}
The Test Class:
@isTest
private class Test_Opportunity_CloseDate_Update {
static testMethod void myUnitTest() {
//create a dummy account
Account a = new Account(Name='Test Account');
insert a;
//Get the Standard PriceBook and make sure it is active
PriceBook2 pb = [select Id from PriceBook2 where IsStandard=True];
pb.IsActive=true;
update pb;
//create two products
List<Product2> prodList = new List<Product2>();
prodList.add(new Product2(Name='Prod1'));
prodList.add(new Product2(Name='Prod1'));
insert prodList;
//Create pricebook entries for the products
List<PricebookEntry> pbEntryList = new List<PricebookEntry>();
pbEntryList.add(new PricebookEntry(Pricebook2Id=pb.Id, Product2Id=prodList[0].id, UnitPrice=100.00, IsActive=true));
pbEntryList.add(new PricebookEntry(Pricebook2Id=pb.Id, Product2Id=prodList[1].id, UnitPrice=200.00, IsActive=true));
insert pbEntryList;
//Get the two record types we care about plus 1 other one.
List<RecordType> recTypeList = [select Id, Name from RecordType where (Name='StandAlone Opportunity' OR Name='Subsidiary Opportunity' OR Name='Primary Opportunity' OR Name='Secondary Opportunity') AND SobjectType='Opportunity'];
//Create 200 new opportunities.
List<Opportunity> newOppList = new List<Opportunity>();
// ***MARIA*** be sure to check StageName... set to something you use.
for(Integer i=1; i<=200; i++){
Opportunity o = new Opportunity(Name='Test Oppty ' + i, CloseDate=Date.Today()+1, AccountId=a.id, StageName='asdfasdf',Probability=0.5);
//We grabbed four record types above, so set 1/4 of opportunities to each record type.
o.RecordTypeId=recTypeList[Math.mod(i,4)].Id;
newOppList.add(o);
}
//insert our new opportunties
insert newOppList;
//Now add both products to all of the opportunities
List<OpportunityLineItem> oliList = new List<OpportunityLineItem>();
for(Opportunity o : newOppList){
oliList.add(new OpportunityLineItem(OpportunityId=o.id, PriceBookEntryId=pbEntryList[0].id,Quantity=1, ServiceDate=Date.Today()+1,UnitPrice=100.00));
oliList.add(new OpportunityLineItem(OpportunityId=o.id, PriceBookEntryId=pbEntryList[1].id,Quantity=1, ServiceDate=Date.Today()+1,UnitPrice=200.00));
}
//insert the opportunity line items
insert oliList;
//Now that everything has been set up, we can begin the actual test
Test.startTest();
//change the close date for all 198 opportunities
for(Opportunity o : newOppList){
o.CloseDate = Date.Today()+7;
}
//Update the opportuntiies
update newOppList;
//get all of the products tied to the opportunites, there should be 396 total
oliList.clear();
oliList = [select Id, ServiceDate from OpportunityLineItem where OpportunityId IN :newOppList];
System.assertEquals(400,oliList.size());
//Now check that the number of Opportunity Line items with service date of Today+7 is 200
//since half the opportunities should have fired product service date updates
Integer plus7Counter = 0;
Integer plus1Counter = 0;
for(OpportunityLineItem oli : oliList){
if(oli.ServiceDate == Date.Today()+7){
//increase the count by 1
plus7Counter++;
}
if(oli.ServiceDate == Date.Today()+1){
plus1Counter++;
}
}
//make sure the counts are as expected
System.AssertEquals(200,plus7Counter);
System.AssertEquals(200,plus1Counter);
Test.stopTest();
}
}










