+ Start a Discussion
JeremyBJeremyB 

Trigger to force an "edit/save" (update) to child records. (oppty --> oppty products)

Problem:  1) Cannot do cross-object workflow
                1a)  Cannot fire workflow off formula field.

So I need a trigger that will force an "edit/save" (that is, an update with no changes necessary) to opportunity products when a user populates a date field, SO_Opened__c,  on the opportunity.

Any help, be it code itself or links to get me further along would be great.

Thanks,

Jeremy

Best Answer chosen by Admin (Salesforce Developers) 
du-scodouglasdu-scodouglas

Alright, so, you just need to have another Opp update that doesn't have SO_Opened__c set, that will get you line 9, for line 13 you need to make sure that you have an OpportunityLineItem in your test method that is inserted with OpportunityId equal to the updating Opportunity (sorry if that is confusing, but you'll have to do that in order to hit line 15).

All Answers

pbattissonpbattisson

Hey Jeremy

 

Can I ask why you need it to perform this update if nothing is being updated? Seems to be a problem with no actual problematic point.

 

Paul

JeremyBJeremyB

good question:

 

It's to get a workflow fired which has a setting of "Every time record is created/edited."

 

 

du-scodouglasdu-scodouglas

Why do you need this workflow to fire? Are you implementing a workflow today, and you want all your legacy data to meet the requirements that your workflow are checking for?

 

If this is the case, you can accomplish what you are trying to do through the Apex Data Loader.

 

Export all the Opportunitys and/or their Products.

Update all the Opportunitys and/or their Products.

 

Please let me know if this solves your issue.

JeremyBJeremyB

nope, doesn't affect legacy records. just this point forward.

 

The workflow does a field update to a formula date field, "Delivery Date" (Delivery_Date__c), on the opp line item. 

 

But in the calculation for this delivery date, it uses the Sales Order opened date as a reference point. So when the Sales Order Opened date (SO_Opened__c) is populated on the opportunity i want that to trigger an update to the line items to have the delivery date populated (via workflow and field udpate).

 

My request seems simple enough, right?

 

Going forward, without worrying about legacy records, when a date field, "SO_Entered__c" is populated on the opportunity, trigger an update to all opp line items -- which causes the workflow field update to fire.

here's me attempting (embarrassing attempt), but hey at least I'm trying.

 

 

trigger <LineItemUpdate> on Opportunity (<after update>) {
for ([OpportunityLineItem[] OppLI : [SELECT Id FROM Opportunity WHERE SO_Entered__c= TRUE]) from Opportunity where Id in :trigger.newMap.keySet()];
    OpportunityLineItem[] OppLI = new OpportunityLineItem[]{}; { 
update OppLI; } 

 

 


du-scodouglasdu-scodouglas

Try this...

 

 

trigger ForceLIUpdate on Opportunity (after update) {

for (Opportunity o : Trigger.new)

{

    if (o.SO_Entered__c == null || o.SO_Entered__c == '')

        continue;

    List<OpportunityLineItem> theLines = [SELECT Id FROM OpportunityLineItem WHERE OpportunityId = : o.Id];

    if (theLines.size() > 0)

       update theLines;


}

}

 

Please let me know if this solves the issue....

 

I enjoy pints of beer. :)

 

JeremyBJeremyB

oh, you are awesome. (and as a diehard hockey fan (sharks), I love all things Canada!)

 

OK, just trying to polish my test class but only getting 50% coverage right now. I'll keep plugging away, but thanks so much again for the help with the trigger!

 

 

@isTest
private Class ForceLIUpdate
{

public static testmethod void testOpportunityTrigger()
{

// In your testmethod you need to create first Account & Opportunity record like
 
Account acc=new Account(Name='Test');
insert acc;
 
Opportunity Oppp=new Opportunity();
oppp.Name='Test Opp';
oppp.Type='New Manufacture';
oppp.RFQ_Received__c=date.today();
oppp.SO_Opened__c=date.today();
oppp.AccountId=acc.id;
oppp.StageName='Proposal/Price Quote';
oppp.CloseDate=Date.today().addDays(15);

insert oppp;

//retrieving the objects I need for creating the OLI.
Opportunity op = [select id from Opportunity where Name like 'OwnerRoleTest #1'];
Pricebook2 pb1 = [select id from Pricebook2 where Name like 'unittest'];
PricebookEntry pbe1 = [select id from PricebookEntry where pricebook2id=:pb1.id];


// Next, it insert a new 2 opportunity product

OpportunityLineItem[] Oli=new OpportunityLineItem[0];

oli.add(new OpportunityLineItem(Opportunityid=op.id,pricebookentryid=pbe1.id,Quantity=1 ,UnitPrice=1000));

insert Oli;


   Product2 prd = new Product2(Name = 'New Manufacture', Description = 'New Manufacture'); 
   insert prd;     



test.startTest();

update Oli;


test.stopTest();
}

}

 

 

du-scodouglasdu-scodouglas

Just make sure that the Opportunity you are updating has SO_Entered__c set to something, in your example it hasn`t been so part of the trigger while never get executed.

 

Don't use existing records in test classes as a bad practice, you'll want to just create all your artificial "test" data within the scope of the testmethod, IE just add Line Items and Product Books to the Oppurtunity oppp.

 

Otherwise, looks good. Pay it forward Jeremy :)

jbardetjbardet

thanks for the heads up with everything, and I most certainly do try to pay it forward, but mostly just in the Answers community where I belong!

 

By the way, I made a mistake originally: SO_Entered wasn't the correct field name. It's actually SO_Opened!

And in the test class I do have SO_Opened__c set to today.

 

Coverage:

 

 1  trigger ForceLIUpdate on Opportunity (after update) {
 2  
 3  for (Opportunity o : Trigger.new)
 4  
 5  {
 6  
 7   if (o.SO_Opened__c == null )
 8  
 9   continue;
 10  
 11   List<OpportunityLineItem> theLines = [SELECT Id FROM OpportunityLineItem WHERE OpportunityId = : o.Id];
 12  
 13   if (theLines.size() > 0)
 14  
 15   update theLines;
 16  
 17  
 18  }
 19  
 20  }

 

 

du-scodouglasdu-scodouglas

Alright, so, you just need to have another Opp update that doesn't have SO_Opened__c set, that will get you line 9, for line 13 you need to make sure that you have an OpportunityLineItem in your test method that is inserted with OpportunityId equal to the updating Opportunity (sorry if that is confusing, but you'll have to do that in order to hit line 15).

This was selected as the best answer
jbardetjbardet

you are the man!

 

With a little help from a friend (Ritesh on the boards), I was able to finish this off nicely.

 

Here is the final code:

trigger ForceLIUpdate on Opportunity (after update) {


List<Id> oppIds = new List<Id>{};


for (Opportunity o : Trigger.new)

{
    if (o.SO_Opened__c != null && o.SO_Opened__c != trigger.OldMap.get(o.Id).SO_Opened__c ) // not null and has changed
         oppIds.add(o.id);
}


    List<OpportunityLineItem> theLines = [SELECT Id FROM OpportunityLineItem WHERE OpportunityId IN :oppIds];

    if (theLines.size() > 0)
         update theLines;

}

 

Test Class

 

@isTest
private Class ForceLIUpdate
{

public static testmethod void testOpportunityTrigger()
{

// In your testmethod you need to create first Account & Opportunity record like
 
Account acc=new Account(Name='Test');


insert acc;
 
Opportunity Oppp=new Opportunity();
oppp.Name='Oppabc';
oppp.Type='New Manufacture';
oppp.RFQ_Received__c=date.today();
oppp.AccountId=acc.id;


oppp.StageName='Closed Won';
oppp.CloseDate=Date.today().addDays(15);
oppp.Send_Quote_By__c=date.today();

insert oppp;

//retrieving the objects I need for creating the OLI.
PricebookEntry pbe1 = [select id from PricebookEntry LIMIT 1];

// Next, it insert a new 2 opportunity product

OpportunityLineItem[] Oli=new OpportunityLineItem[]{};


oli.add(new OpportunityLineItem(Opportunityid=oppp.id,pricebookentryid=pbe1.id,Quantity=1 ,UnitPrice=1000));

insert Oli;


test.startTest();

Oppp.SO_Opened__c = date.today();
update oppp;



test.stopTest();
}

}

           

 

ben_sdaben_sda

Hi All:

 

Thanks for the code, I have 2 custome objects and the same problem.

In line:

 

List<Operation__c> theLines = [SELECT Id FROM Operation__c WHERE OpportunityId IN :oppIds];

 

what should I replace my code with.

 

In my case, parent Object is: Developments and chils object is: Monitoring

Parent Object field is: Overal Risk and child object risk is: PER

 

Do I need to mention the name of the Workflow as well?

Do I need to mention the Child object filed name as well?

 

Regards,

 

Ben