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
Craig PCraig P 

map custom Lead lookup field (Product__c) to Oppty Product

I added a custom lookup field (Product__c) to the Lead object and need to map it to the Opportunity standard field.  I have the code to add the Pricebook Entry to the Opportunity Line Item, but I cannot get the Product2Id from my custom lookup field (Lead.Product__c)

 

How do I get the Product ID (or Pri

 

 

trigger trgLeadConvert on Lead (after update)

{   

    for(Lead l : Trigger.new) 

    {       

           if(l.IsConverted && l.convertedOpportunityId != null)       

          {           

                     Opportunity oppty = [Select o.Id, o.Description from Opportunity o Where o.Id = :l.ConvertedOpportunityId];                           

                     // add an opportunity line item...           

                     OpportunityLineItem oli = new OpportunityLineItem();           

                     oli.OpportunityId = oppty.Id;           

                     oli.Quantity = 1;           

                     oli.TotalPrice = 100.00;           

                     

                     // THIS IS I NEED HELP!!!!

                     //oli.PricebookEntryId = l.Product__c.PricebookEntry.Product2Id;           

                     //oli.PricebookEntryId = l.Product__c.Id;           

 

                     insert oli;
       }   

}

}

Best Answer chosen by Admin (Salesforce Developers) 
sfdcfoxsfdcfox

Pricebook Entry is a child of Products, and therefore can't be referenced the way you're attempting to do so. Instead, you'll need a trigger like this (tested):

 

trigger trgLeadConvert on Lead (after update) {
  // products
  Set<Id> products = new Set<Id>();
  // opportunities
  set<id> opportunities = new set<id>();
  // product to pricebook entries
  map<id,pricebookentry> pricebookentries = new map<id,pricebookentry>();
  // line items to add
  list<opportunitylineitem> lineitems = new list<opportunitylineitem>();
  // standard pricebook
  pricebook2 standardpricebook = [select id from pricebook2 where isstandard = true];

  // get all product and opportunity id values
  for(lead l:trigger.new) {
    products.add(l.product__c);
    opportunities.add(l.convertedopportunityid);
  }

  // map product id to pricebook entry
  for(pricebookentry pbe:[select id,unitprice,product2id from pricebookentry where pricebook2.isstandard = true and product2id in :products])
    pricebookentries.put(pbe.product2id,pbe);

  // set pricebook entry value for opportunities
  for(opportunity[] opps:[select id from opportunity where id in :opportunities]) {
    for(opportunity opp:opps)
      opp.pricebook2id = standardpricebook.id;
    update opps;
  }
  
  // create line items
  for(lead l:trigger.new) {
    if(l.convertedopportunityid == null || l.product__c == null)
      continue;
    lineitems.add(new opportunitylineitem(quantity=1,totalprice=pricebookentries.get(l.product__c).unitprice,opportunityid=l.convertedopportunityid,pricebookentryid=pricebookentries.get(l.product__c).id));
  }
  
  // commit line items to the database
  insert lineitems;
}

Take a look and let me know what you think.

All Answers

sfdcfoxsfdcfox

Pricebook Entry is a child of Products, and therefore can't be referenced the way you're attempting to do so. Instead, you'll need a trigger like this (tested):

 

trigger trgLeadConvert on Lead (after update) {
  // products
  Set<Id> products = new Set<Id>();
  // opportunities
  set<id> opportunities = new set<id>();
  // product to pricebook entries
  map<id,pricebookentry> pricebookentries = new map<id,pricebookentry>();
  // line items to add
  list<opportunitylineitem> lineitems = new list<opportunitylineitem>();
  // standard pricebook
  pricebook2 standardpricebook = [select id from pricebook2 where isstandard = true];

  // get all product and opportunity id values
  for(lead l:trigger.new) {
    products.add(l.product__c);
    opportunities.add(l.convertedopportunityid);
  }

  // map product id to pricebook entry
  for(pricebookentry pbe:[select id,unitprice,product2id from pricebookentry where pricebook2.isstandard = true and product2id in :products])
    pricebookentries.put(pbe.product2id,pbe);

  // set pricebook entry value for opportunities
  for(opportunity[] opps:[select id from opportunity where id in :opportunities]) {
    for(opportunity opp:opps)
      opp.pricebook2id = standardpricebook.id;
    update opps;
  }
  
  // create line items
  for(lead l:trigger.new) {
    if(l.convertedopportunityid == null || l.product__c == null)
      continue;
    lineitems.add(new opportunitylineitem(quantity=1,totalprice=pricebookentries.get(l.product__c).unitprice,opportunityid=l.convertedopportunityid,pricebookentryid=pricebookentries.get(l.product__c).id));
  }
  
  // commit line items to the database
  insert lineitems;
}

Take a look and let me know what you think.

This was selected as the best answer
Craig PCraig P

sfdcfox,

 

Thank you very much!  That worked very well.  And the code is very simple, concise and makes sense.

 

The bottom line is that I need to load the IDs in memory & then do a "join" on those IDs, using the Map object (pricebookentries).

 

Question:

Why is the standard pricebook object called pricebook2?

 

Steps.

  1. Step 1:  get all of the Lead object's id values (in this case, Product & Opportunity)
  2. Step 2:  join the Lead object's selected values (Product) w/ the database (Priceentrybook) to get the specific product2ID value
  3. Step 3:  update the Lead's converted Opportunity price book w/ the correct Price Book
  4. Step 4:  add line item to the converted Opportunity
sfdcfoxsfdcfox

The standard pricebook and product objects were changed to Pricebook2 and Product2, respectively, because they made some changes to the API that were incompatible with older clients. In order to allow older clients to still use the API, they simply made a new copy of the objects with the appropriate changes. These objects replicate changes made in one to the other so they are always in synch. This all happened quite a few years ago, and is really quite unremarkable in nature. Just know that the "2" suffix exists for the sake of client compatibility.

 

You'll find that most triggers in salesforce.com will follow this very same pattern; I call this the "aggregate-query-update" design, and most triggers will follow this design fairly closely. Keep this code handy, as you'll probably be using a very similar design in future triggers. It seems you're catching on pretty quickly with triggers, so you shouldn't have too many more problems, but do feel free to ask if you need any more assistance. That's what the community is here for.

Craig PCraig P

Thanks again, the "aggregate-query-update" pattern will is useful to know.  Is there a site that lists all of the SFDC design patterns that I should know?

sfdcfoxsfdcfox

There's plenty of blogs out there; just check the users of the most frequent responses. I myself am working on a blog of my own, where I hope to eventually list all the patterns, and other tips and tricks. This method I outlined, however, is probably the most common design pattern. You can use it for rollup summary triggers, data copy triggers, data translation triggers (i.e. converting country names to standard formats), validation triggers, and other common use cases. It's only when you get to the esoteric cases where you'll find  you need a different design pattern. I'm going to have to sit down and think about it, but I'm sure I myself will have a list available soon.