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
ckellieckellie 

Copying more than one item

I have written a trigger to copy the opportunity line item as a quote line item, but the trigger is only copying one record, not all of the opportunity line items attached to the opportunity. How do I add a loop to copy as many opportunity line items to quote line items as are attached to the opportunity?

 

Trigger:

 

trigger NewQuoteLineItems on Quote (After Insert) {

    Set<Id> qid = new Set<Id>();
    for(Quote q : Trigger.new){
        System.debug('**** 0 q id : '+q.Opportunityid); 
        qid.add(q.opportunityid);
    }
    System.debug('****1 : q Id size '+ qId.size());
    
    List<quote> qu = new List<Quote>([select id, opportunityid
             from quote where opportunityid in:qid]);
    
    List<Opportunity> o = new List<Opportunity>([select id, Pricebook2Id from opportunity
            where id in:qid]);
    
    List<OpportunityLineItem> oli = new List<OpportunityLineItem>([select id, opportunityid, pricebookentryid,
        Quantity, UnitPrice 
        from OpportunityLineItem where opportunityid = :qid]);

    System.debug('****2 : q Id size '+ qId.size());
   
   List<QuoteLineItem> qli = new List<QuoteLineItem>();   
       System.debug('****2 : qli Id size '+ qli.size());
    for(Quote quote : System.Trigger.new){
           if(Quote.id != null) {
        
            qli.add(New QuoteLineItem (
            quoteid = qu[0].id,
            pricebookentryid = oli[0].pricebookentryid,
            UnitPrice = oli[0].UnitPrice,
            Quantity = oli[0].Quantity));
            
            }
    }
    insert qli;
}

 

Thank you

Best Answer chosen by Admin (Salesforce Developers) 
SteveBowerSteveBower

Well, what is supposed to be happening is equivalent to:

 

List<OpportunityLineItem> x;

 

x = oliMap.get(oli.OpportunityId);

x.add(oli);

oliMap.put(oli.OpportunityId, x);

 

So, perhaps it's confused by putting it all on one line?   Or maybe something isn't declared properly?  Best, Steve.

 

All Answers

SteveBowerSteveBower

So I'm getting that you want an after insert trigger on Quote that, for each Quote,  takes all the OLI's for the related Opp and inserts them as QLI's.

 

trigger NewQuoteLineItems on Quote (After Insert) {
    
    // Make a List of Opportunity ID's for all these quotes.
    Set<Id> oppIds = new Set<Id>();
    for(Quote q : Trigger.new) oppIds.add(q.opportunityid);
    
    // Fetch all the Opportunity Line Items for all these Opportunities
    List<OpportunityLineItem> olis = new List<OpportunityLineItem>([
        select 
             id, opportunityid, pricebookentryid, Quantity, UnitPrice 
        from OpportunityLineItem 
        where opportunityid = :OppIds
    ]);
    

    // Build a Map, keyed by OppId, of Lists of the related OLI's
    Map<Id, List<OpportunityLineItem>> oliMap = new Map<Id, List<OpportunityLineItem>>();
    for (OpportunityLineItem oli: olis) {
       if (oliMap.containsKey(oli.OpportunityId) {
            // If the map already has an entry for this Opp, add this OLI to the list.
            oliMap.put(oli.OpportunityId, oliMap.get(oli.OpportunityId).add(oli));
       } else {
            // This is the first entry for this Opportunity
            List<OpportunityLineItem> tmp = new List<OpportunityLineItem>();
            tmp.add(oli);
            oliMap.put(oli.OpportunityId, tmp);
       }
    }

   List<QuoteLineItem> qli = new List<QuoteLineItem>(); 
  
   // Iterate through each Quote
   for(Quote q : Trigger.new){
       // Do we have any OLI's for this Quotes Opportunity?
       if (oliMap.containsKey(q.opportunityId)) {
           // Yes, so for each OLI in the List, create a QLI
           for (OpportunityLineItem oli: oliMap.get(q.opportunityId)) {
                qli.add(
                     New QuoteLineItem (
                         quoteid = q.id,
                         pricebookentryid = oli.pricebookentryid,
                         UnitPrice = oli.UnitPrice,
                         Quantity = oli.Quantity
                     )
                );
           }
       }
    }
    if (! qli.isEmpty()) insert qli;
}

 

Perhaps this is what you want.  Note that I'm just typing it in freehand and so I haven't tried to compile it.  I'm sure there's probably a syntax/spelling error or two in there, but hopefully this gives you the idea.

 

Note that doing the whole Map creation thing isn't strictly needed.  instead of that you could just iterate throuh the entire set of OLI's for each quote and see if the Opportunity ID's match.   Of course that's pretty inefficient for larger numbers of records.

 

Best, Steve.

 

 

 

 

 

ckellieckellie

Steve,

 

Thanjk you for the code. I have been studying the code and have been able to further adapt the code, except for this line:

 

 oliMap.put(oli.OpportunityId, oliMap.get(oli.OpportunityId).add(oli));

 where I am recieving this error:

 

	Error: Compile Error: Incompatible value type Object for MAP<Id,LIST<OpportunityLineItem>> at line 21 column 13

 What does the error mean and why? how do I solve the error?

 

Thank you

SteveBowerSteveBower

Well, what is supposed to be happening is equivalent to:

 

List<OpportunityLineItem> x;

 

x = oliMap.get(oli.OpportunityId);

x.add(oli);

oliMap.put(oli.OpportunityId, x);

 

So, perhaps it's confused by putting it all on one line?   Or maybe something isn't declared properly?  Best, Steve.

 

This was selected as the best answer
ckellieckellie

Thank you. Separating the code into multiple lines was the key. I also understand more about maps through your help. Thank you Steve

Kanika DuaKanika Dua
The above error can be solved by replacing code line 
 oliMap.put(oli.OpportunityId, oliMap.get(oli.OpportunityId).add(oli));
By
 oliMap.put(oli.OpportunityId, oliMap.get(oli.OpportunityId));