+ Start a Discussion
bikla78bikla78 

Opportunity Trigger - Update Product2 field

I created a trigger that updates the account.status field by checking all opportunities associated with that account. If all opportunities are ended then it will update the account to former client if not, it will change it to current client. This logic works but now I am also trying to update the product associated with all opportunities. An update needs to happen to the product2.status field but I can't  figure out how to tie the opportunity.id  =  opportunitylineitem.opportunityid =opporutnitylineitem.pricebookentryid = pricebookentry.product2id = product2.id

 

My goal is so that if all opportunities associated with that product have a stage = ended then the product2.status would be updated to "Ready to be deployed" but  if at least 1 of those opportunities has stage= in process, then product2.status = 'Deployed" . This should update the product2 once the opportunity is created or updated.

 

As you can see I am using arrays.

 

trigger Opportunity_To_Account on Opportunity (before insert, before update) {

   Opportunity_Management.UpdateAccountStatus(trigger.new);

}

 

//--------------------------------------------------------------------------------------------------------------

// This class is used to automate various steps for Sales Opportunity Management for the CSDs

//--------------------------------------------------------------------------------------------------------------

public class Opportunity_Management

{

   public static void UpdateAccountStatus(Opportunity[] Opp)

    {

        for (Opportunity a:Opp)

        {

        if (a.accountId != null)

        {

            Opportunity[] Opps = null;

            OpportunityLineItem[] OpplineItem = null;

            Integer nEnded = 0;

            Integer nInProcess = 0;

 

           try     

           {

             Opps = [select Id, StageName From Opportunity where AccountId=:a.AccountId];

             Opplineitem = [select OpportunityId, PricebookEntryID From OpportunityLineItem where OpportunityId=:a.Id];

 

             for (Opportunity Opp2 :Opps)

             {

//-----------------------------------------------------------------------------------------------------------------

// When an opportunity stage is ended, it assigns it to the array with variable nEnded. It checks all opportunities

// with this stage for that particular account, (a.id)

//----------------------------------------------------------------------------------------------------------------

                   if (Opp2.Stagename == 'Won-Engagement Ended' && Opp2.Id <> a.Id)

                   {

                       nEnded = nEnded + 1;

//-----------------------------------------------------------------------------------------------------------------------

// When an opportunity stage is in process, it assigns it to the array with variable nInProcess.It checks all opportunities

// with this stage for that particular account, (a.id)

//------------------------------------------------------------------------------------------------------------------------

                   }

                  if (Opp2.Stagename == 'Won-Engagement in Process' &&  Opp2.Id <> a.Id)

                   {

                       nInProcess = nInProcess + 1;

                   }

                 }

 

                 if (a.Stagename == 'Won-Engagement in Process')

                   {

                       nInProcess = nInProcess + 1;

                   }

 

           }

 

           catch(Exception e)

           {

           system.debug(e);

           }

 

 

          Account acct = [select id from Account where Id = :a.accountid];

        // ??  Product2 prod = [select id from Product2 where Id]

 

 

          if (nInProcess > 0)

             {

              acct.Accountstatus__c= 'Current Client - Billing';

              acct.Type = 'Client';

             // ??? prod.status = 'Deployed'

 

 

              }

          if (nEnded > 0  && nInProcess < 1)

              {

              acct.Accountstatus__c= 'Former Client - Not Billing';

              // ??? prod.status = 'Ready to be deployed'

 

              }

           try

           {

            update acct;

            }

 

           catch (System.DmlException e) {

           system.debug(e.getMessage());

 

           }

 

          }

 

        }

 

     }

EMHDevEMHDev

I could understand if you wanted to change the opportunity line item status, but are you sure you want to change a product status dynamically from a trigger?  Products are normally fairly static data - and each product would typically be used by multiple opportunities, time and time again, so the status is really something that should sit in the opportunity or opportunity line item, rather than in the product2 table.  Can you could explain how you use the products? 

 

Erica

bikla78bikla78

Hi Erica- thank you for asking

 

Currently, our products are used as consultant records in our database that are tied to opportunities. We use the opportunitylineitem to identify the sales price. However, we need to find a way so that consultants are associated with a status so the sales team will know if they are currently deployed, ready to be deployed. So if we used a trigger against the opportunity object, we could essentially check all products and update their status accordingly.

 

Thanks,

 

 

EMHDevEMHDev

OK, so presumably the status field is a custom field in Product2.  I've done something similar, although I don't update the Product2 item.  I use an outer class to get the Product2 data into a static List.  You need a number of queries to trace the records through, I'll post the various ones and see if you can tie them in, as my code is really complicated and posting it all will probably confuse more than help.

 

In the outer class, I use this List:

 

public static final List<Product2> allProducts = [Select p.Id, p.Family From Product2 p  where p.IsDeleted=false and p.IsActive=true];

 

Obvious your where clause would probably be different.  This gives you a list of all products (or in your case, all consultant records).  You could tailor the where clause to select only those records for your consultant, if that is a field in Product2.

 

I have a trigger on the OpportunityLineItem as well as the Opportunity because I need to change a total every time an Opportunity Line Item is changed.  This then updates the Opportunity, which causes the other trigger to fire.  Having a chain of triggers like this works, but gives you a lot of considerations when it comes to governor limits, so beware, but I suspect that you need a separate trigger on your Opp line item in order to achieve what you need.

 

In the Opportunity, I do the following:

 

	Set<Id> AccIds = new Set<Id>();

Map<Id, Id> OppAccMap = new Map<Id, Id> (); // Map of Opportunity to Account for all opps in trigger

for (Opportunity opp:trigger.new){
AccIds.add(opp.AccountId); // Add the account ID to the set
OppAccMap.put(opp.AccountId, opp.Id); // Add the opp/account pairing
}
Map<Id, Account> AccountSet = new Map<Id, Account>([Select Type from Account where Id in :AccIds]); // Map of Account Id to Account Type

// Loop through all opportunities
for (Opportunity opp:trigger.new){
// check the account type or whatever by checking the AccountSet defined above for the Opportunity

}

// This query gives us the product2IDs of the products that are in the desired product grouping,
Set<Id> prodset = new Set<Id>();
Map<Id, List<OpportunityLineItem>> oliMapLoc;

for (Product2 prod :OppLineItems.allProducts){ // i.e. the outerclass list
if ((prod.??? == 'your criteria') {

prodset.add(prod.Id);
}
}

 

 

I then get a map from my outer class (so that I only do it when I need to, again because of governor limits):

 

	public static Map<id, List<OpportunityLineItem>> getOliMap(List<Opportunity> oppList) {
ID oid;
List<Id> oidList = new List<Id>();
OpportunityLineItem oli;
List<OpportunityLineItem> oppoliList = new List<OpportunityLineItem>();

// Build a list of opportunity ids
for (Integer i=0; i<oppList.size(); i++){
oidList.add(oppList.get(i).Id);
}
// build a list of all opportunity line items for all of those ids so we use one SOQL statement
List <OpportunityLineItem> oliList = [Select TotalPrice, Line_List_Price__c, PricebookEntry.Name, PricebookEntry.Product2Id, OpportunityId, PricebookEntryId, <status? consultant?>, Id
From OpportunityLineItem where OpportunityId IN :oidList];
// build a map of opportunity ID to list of line items for that opportunity
for (Integer i=0; i<oidList.size(); i++){
oppoliList.clear();
oid = oidList[i];
for (Integer k=0; k<oliList.size(); k++){
oli=oliList[k];
if (oli.OpportunityId == oid)
oppoliList.add(oli);
}
oliMap.put(oid, oppoliList);

}
DoneMap=true;
return oliMap;
}

 

 

My OpportunityLineItem trigger looks something like this:

 

	    Set<Id> PBEIdSet = new Set<Id>();

//We loop through the updated records to get the Id, and add it to the set
for(OpportunityLineItem oli : trigger.new) { // Yours would be looping through the opportunities on your account)
if(oli.PriceBookEntryId != null){
PBEIdSet.add(oli.PriceBookEntryId);
}
}


Map<id, PriceBookEntry> pbeMap = new Map<id, PriceBookEntry>([
SELECT id, Product2.Name, Product2.<Status?> FROM PriceBookEntry WHERE id in :PBEIdSet and <another other criteria>]);

for(OpportunityLineItem oli : trigger.new) {
/* update your line item if necessary (line is a before update trigger) */

}

if(pbeMap.containsKey(oli.PriceBookEntryId)){ // criteria here
oli.<update field here on the line item>
}
}
}

 

I know this isn't exactly your case, but hopefully it helps to point you towards the queries you need and ideas of how to process the line items.

 

 

Speaking of governor limits, you are better off putting your SOQL queries outside of your for loop, by creating a list or map which you then traverse with your logic, as I've done above.

 

Hope that helps -

Erica