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
Nick KeehanNick Keehan 

Apex Trigger - Convert String of products to OpportunityLineItems

Hi guys.

Looking to create an Apex trigger to fire on the Opportunity to convert a Long Text Area String to opportunityLineItems. I have a pricebook set on the opportunity already, so i think i am looking for a type of map that looks at the pricebook entry from the name and supplies a PricebookEntryID, then creating a loop of Opportunity Line Items with the entrys.

E.g. On Opportunity - Product_Names__c - Product 1, Product 2, Product 3

I can have the string seperated/formated in a number of ways, semi colon, no space etc.

Does anyone have any idea how this would look? sorry, still new with Maps.

Nick
Best Answer chosen by Nick Keehan
Shweta_AgarwalShweta_Agarwal
Hi,

You can use following code,

trigger oppLineItem on Opportunity (after insert,after update) {
    Map<id,Set<id>> oppIdProNameMap = new Map<id,Set<Id>>();
    Set<Id> priceBookIdSet = new Set<id>();
    if(checkRecursive.runOnce()){
    for(Opportunity opp : trigger.new){
        priceBookIdSet = new Set<Id>();
        system.debug('---opp.Product_Names__c--'+opp.Product_Names__c);
        if(opp.Product_Names__c != ''){
            priceBookIdSet=getPriceBookId(opp.Product_Names__c,opp.Pricebook2Id); 
            system.debug('---priceBookIdSet---'+priceBookIdSet);
            oppIdProNameMap.put(opp.Id, priceBookIdSet);
        }            
    }
    List<OpportunityLineItem> newLineItemsList = new List<OpportunityLineItem>();
    for(id oppId : oppIdProNameMap.keySet()){
        for(id pBId : oppIdProNameMap.get(oppId)){
            OpportunityLineItem lineItem = new OpportunityLineItem (OpportunityID= oppId,PriceBookEntryID=pBId, quantity=1,TotalPrice = 100);
            newLineItemsList.add(lineItem);
        }    
    }
    insert newLineItemsList;
    } 

    public Set<id> getPriceBookId(string pName,id bookId){
        List<String> proName = new List<String>();
        proName = pName.split(',');        
        Map<id,product2> proIdMap = new Map<id,product2>([Select id from product2 where name IN: proName]);
        system.debug('----proIdMap.keySet()--'+proIdMap.keySet());
        Map<id,PricebookEntry> priceBookMap = new Map<id,PricebookEntry>([SELECT Id,IsActive,Product2Id,UnitPrice FROM PricebookEntry WHERE Product2Id IN: proIdMap.keySet() and Pricebook2Id =: bookId]);
        return priceBookMap.keySet();
    }
}

Hope it will solve your problem.

Thanks,
Shweta

All Answers

swati_sehrawatswati_sehrawat
your trigger should be something like, Please modify as per your requirement.
 
list <OpportunityLineItem> listToInsert = new list <OpportunityLineItem>();
for(Opportunity obj : trigger.new){
	if(string.isNotBlank(obj.OpportunityLineItemLongTextAreaField)){
		for(string temp : obj.OpportunityLineItemLongTextAreaField.split(';')){
			OpportunityLineItem newObj = OpportunityLineItem();
			OpportunityLineid = obj.id;
			//put other values in OpportunityLineItem as per your need
			listToInsert.add(newObj);
		}
	}
}
if(listToInsert!=null){
	insert listToInsert;
}

 
Shweta_AgarwalShweta_Agarwal
Hi,

You can use following code,

trigger oppLineItem on Opportunity (after insert,after update) {
    Map<id,Set<id>> oppIdProNameMap = new Map<id,Set<Id>>();
    Set<Id> priceBookIdSet = new Set<id>();
    if(checkRecursive.runOnce()){
    for(Opportunity opp : trigger.new){
        priceBookIdSet = new Set<Id>();
        system.debug('---opp.Product_Names__c--'+opp.Product_Names__c);
        if(opp.Product_Names__c != ''){
            priceBookIdSet=getPriceBookId(opp.Product_Names__c,opp.Pricebook2Id); 
            system.debug('---priceBookIdSet---'+priceBookIdSet);
            oppIdProNameMap.put(opp.Id, priceBookIdSet);
        }            
    }
    List<OpportunityLineItem> newLineItemsList = new List<OpportunityLineItem>();
    for(id oppId : oppIdProNameMap.keySet()){
        for(id pBId : oppIdProNameMap.get(oppId)){
            OpportunityLineItem lineItem = new OpportunityLineItem (OpportunityID= oppId,PriceBookEntryID=pBId, quantity=1,TotalPrice = 100);
            newLineItemsList.add(lineItem);
        }    
    }
    insert newLineItemsList;
    } 

    public Set<id> getPriceBookId(string pName,id bookId){
        List<String> proName = new List<String>();
        proName = pName.split(',');        
        Map<id,product2> proIdMap = new Map<id,product2>([Select id from product2 where name IN: proName]);
        system.debug('----proIdMap.keySet()--'+proIdMap.keySet());
        Map<id,PricebookEntry> priceBookMap = new Map<id,PricebookEntry>([SELECT Id,IsActive,Product2Id,UnitPrice FROM PricebookEntry WHERE Product2Id IN: proIdMap.keySet() and Pricebook2Id =: bookId]);
        return priceBookMap.keySet();
    }
}

Hope it will solve your problem.

Thanks,
Shweta
This was selected as the best answer
Nick KeehanNick Keehan
Thanks Shweta.

Getting an issue with the variable (Compile Error: Variable does not exist: checkRecursive at line 4 column 8).

Any idea on how to overcome?

Nick
Shweta_AgarwalShweta_Agarwal
Hi Nick,

checkRecursive is a class to handale recursion. When we insert opportunity line item it will update the opportunity, which will cause recursion.
Find the code for checkRecusive

public Class checkRecursive{
    private static boolean run = true;
    public static boolean runOnce(){
    if(run){
     run=false;
     return true;
    }else{
        return run;
    }
    }
}

Thanks,
Shweta
Nick KeehanNick Keehan
Thanks Shweta, that makes sence.

This works brilliantly, except for two minor exceptions.

1:   We cant add two of the same product into the string field. Quite often there will be a situation where an agent could sell two of the same product. The trigger only adds the first item rather than the list of items. it must pick it up as remove it as the line item has been added.
2:   It will not allow us to create normal opportunities as its trying to add line items each time.  if(opp.Product_Names__c != '') doesnt seem to be firing. normally there is nothing in the string field however it is still giveing me a trying to reference a null object.

Any suggestions here?

Thanks for your help by the way!
Nick KeehanNick Keehan
Hi Shweta. I have solved number 2.

Just looking to see if i can allow this process to allow multiple of the same opportunity Product.

E.g. String -" Line and Broadband,CallerID,CallerID

Currently it removes these from the list of products being added.

Nick
Shweta_AgarwalShweta_Agarwal
Hi Nick,

I have done few changes in above trigger. Hope it will solve your both problem.

trigger oppLineItem on Opportunity (after insert,after update) {
    Map<id,Map<string,id>> oppIdProPriceMap = new Map<id,Map<string,id>>();
    Map<string,id> mapOfPriceProName = new Map<string,id>();
    Map<id,List<string>> oppIdProNameMap = new Map<id,List<string>>();
    List<String> proName = new List<String>();
    if(checkRecursive.runOnce()){
    for(Opportunity opp : trigger.new){
        mapOfPriceProName = new Map<string,id>();
        system.debug('---opp.Product_Names__c--'+opp.Product_Names__c);
        if(opp.Product_Names__c != null){
            mapOfPriceProName=getPriceBookId(opp.Product_Names__c,opp.Pricebook2Id); 
            system.debug('---mapOfPriceProName---'+mapOfPriceProName);
            oppIdProPriceMap.put(opp.Id, mapOfPriceProName);
            oppIdProNameMap.put(opp.Id, proName);
        }            
    }
    List<OpportunityLineItem> newLineItemsList = new List<OpportunityLineItem>();
    for(id oppId : oppIdProNameMap.keySet()){
        for(String pName : oppIdProNameMap.get(oppId)){
            system.debug('----oppIdProPriceMap.get(oppId).get(pName)---'+oppIdProPriceMap.get(oppId).get(pName));
            if(oppIdProPriceMap.get(oppId).get(pName) != null){
                OpportunityLineItem lineItem = new OpportunityLineItem (OpportunityID= oppId,PriceBookEntryID=oppIdProPriceMap.get(oppId).get(pName), quantity=1,TotalPrice = 100);
                newLineItemsList.add(lineItem);
            }
        }    
    }
    insert newLineItemsList;
    } 

    public Map<string,id> getPriceBookId(string pName,id bookId){
        proName = new List<String>();
        proName = pName.split(','); 
        Map<id,product2> proIdMap = new Map<id,product2>([Select id from product2 where name IN: proName]);
        system.debug('----proIdMap.keySet()--'+proIdMap.keySet());
        system.debug('-----bookId----'+bookId);
        List<PricebookEntry> priceBookList = [SELECT Id,IsActive,Product2Id,Product2.Name,UnitPrice FROM PricebookEntry WHERE Product2Id IN: proIdMap.keySet() and Pricebook2Id =: bookId];
        Map<string,id> proNameEntryIdMap = new Map<string,id>();
        for(PricebookEntry price : priceBookList){
            proNameEntryIdMap.put(price.Product2.Name, price.id);    
        }
        return proNameEntryIdMap;
    }
}

Thanks
Salesforce####Salesforce####
@hello  shweta a k ; 

i have a small doubt , can you correct me as i am new working on the price books .  i did not understand where my trihgger is firing .

i created a new longtext area field named  "Product_Names__c " in opportunity and i placed your code in my org . 

so while creating a new opportunity , i gave the values in field named  "Product_Names__c " as "test1 , test2 ,test3  ".and i created a price boxfor this clicked on  so i hope these all values should be falling as a related list . 

but this is not happening am i doing some thing wrong . i did not understand where my trihgger is firing .my org pic

 
Shweta_AgarwalShweta_Agarwal
Hi 

Can you check once if the price book for the product name which you entered in Product_name__c and price book for opportunity is same. This code will add only those product in realted list which are from same price book.

Thanks
Salesforce####Salesforce####
hello Shweta  , 

thanks for explaining me , i tried this now  but couldnt achieve this , can you please correct me am doing wrong  as i  want to learn this so i am posing you questions . 

Step 1:
Price book  name : test , which contains the below products 

User-added image






Step2 :
I created a new opportunity ,  with the product names same as below and I saved my opportunity .
User-added image

 
I hope your trigger should display those product names in related list of the products . but this is not happening , can you please tell me the step by step how to achieve this functionality works
 
 
Shweta_AgarwalShweta_Agarwal
After saving the opportunity you need to add pricebook for particular opportunity. For that you need to click on "Choose price book " button.
It will redirect you to pricebook selection page. when you select the pricebook "Test" and click on save then the trigger will fire and will add the products .
Salesforce####Salesforce####
hello shwta_agarwal : can i request you to put screen shot of steps  in a step by step manner which you excecuted in your org in order to get the trigger fired , which will be extremely helpful for me . please kindly help me