[SalesForce] Passing Product from Lead to Opportunity on Lead Conversion

My company is interested in having Salesforce automatically create Opportunity line items upon lead conversion, based on data on the lead.

What I have set up are lookup fields on the lead for Product, and Price Book. I then mapped these fields on lead conversion mapping to corresponding custom fields on the opportunity. When I convert a lead, these lookup fields are successfully passing their data from the lead to the newly created opportunity (which gets created at the time of conversion). However, it does not appear that it is possible to create opportunity line items at the same time that the opportunity gets created. I created a trigger that should have worked, however no line items get created, and no errors are thrown. After doing a little research, I found this suggestion, along with their own trigger, which appears to clone the opportunity and then the opportunity line items are inserted to the cloned opportunity, here:

https://developer.salesforce.com/forums/?id=906F00000008oyNIAQ

This trigger doesn't even compile, but it gave me a good starting point. I'm now getting an error when I try to convert leads with an error message that's not very helpful.

Error: System.DmlException: Insert failed. First exception on row 0;
first error: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY,
trgAddProductFromLead: execution of AfterInsert caused by:
System.DmlException: Update failed. First exception on row 0; first
error: MISSING_ARGUMENT, Id not specified in an update call: []
Trigger.trgAddProductFromLead: line 29, column 1: []
Class.leadconvert.BulkLeadConvert.handleOpportunityInserts: line 737,
column 1 Class.leadconvert.BulkLeadConvert.convertLead: line 104,
column 1

I've posted my trigger below. Let me know if you need any additional information from me, I'm pretty stumped!

trigger trgAddProductFromLead on Opportunity (after insert) {

  Set<ID> oppProdId = new Set<ID>();                                   //Builds Product Ids to use in query

  Set<ID> oppPBId = new Set<ID>();                                    //Builds PriceBook Ids to use in query

  List<Opportunity> oppList = new List<Opportunity>();                 //Builds list of Opportunities to update the Price Book ID field

  List<OpportunityLineItem> oliList = new List<OpportunityLineItem>(); //Set used to create Opportunity Products

  Map<ID, ID> pbeMap = new Map<ID, ID>();                              //Map used to match Products to PriceBookEntry records

  for(Opportunity o : Trigger.new) {

      if(o.Product__c != null && o.Price_Book__c != null) {       //Only use the Opportunity if it is created with a Product__c and Price_Book__c value (i.e. - On Convert)

          Opportunity otemp = o.clone(false, false, false, false); 

          oppProdId.add(o.Product__c);                             //Add the product id to the set     

          oppPBId.add(o.Price_Book__c);                            //Add the pricebook id to the set                       

          oppList.add(otemp);

      }

  }

    update oppList;                                                      //Update all of the Opportunities with teh Price Book 

  for(PriceBookEntry pbe : [select Id, Product2Id from PriceBookEntry where PriceBook2Id in :oppPBId and Product2Id in :oppProdId]) {

      pbeMap.put(pbe.Product2Id, pbe.id);                              //Build the map to use to match Procucts to PriceBookEntries

  }

  for(Opportunity opp : oppList) {                                     //Build the OpportunityLineItems for each Opportunity (sets the quantity to 1 and the price to 100)

      OpportunityLineItem oli = new OpportunityLineItem(OpportunityId = opp.id, PriceBookEntryId = pbeMap.get(opp.Product__c), Quantity = 1, UnitPrice = 100);

      oliList.add(oli);

  }

  if(oliList.size() > 0) {

      insert oliList;                                                  //Create the OpportunityLineItems


    }
        }

Alternatively, if there is a better way to go about this (I have not found one online), I'm willing to try something else!

Best Answer

The specific reason for your error is in the clone of the Opportunity

Opportunity otemp = o.clone(false, false, false, false); 

In the doc, the first argument is whether to preseve the ID, you have false which does not preserve the ID. Hence, the update statement has no IDs.

You have to worry about trigger recursion as well since updating the Opportunity with the pricebook2ID will also cause the same trigger to reinvoke.

Since Lead Conversion lands the user on the Account page, you could probably move all this logic into a future method and avoid the recursion issue. By the time the user comes to the Opportunity page, the future will (most likely) be done.

Related Topic