Take the 2-minute tour ×
Salesforce Stack Exchange is a question and answer site for Salesforce administrators, implementation experts, developers and anybody in-between. It's 100% free, no registration required.

I'm trying to find out if a Quote Line Item has any reference back to which Opportunity Line Item it relates to. Does it??

The reason is that I have extra fields on the OLI which I need to pull into the QLI and so need to do something that can pull this info into the QLI. Ideally, I'd like a 'point and click solution' (e.g. workflow or formula) to do this - is it possible? I know I can work it out in a trigger using the Quote.Opportunity reference, the pricebook2id and other fields and look for a match but this feels like too much effort to do something simple.

Something like the 'Map Lead Fields' when converting a Lead would be ideal in this case...

share|improve this question
    
Have a look at this app exchange package, it's unmanaged so you can have a look at the code:appexchange.salesforce.com/… –  Girbot Aug 20 '14 at 10:38

4 Answers 4

up vote 3 down vote accepted

As @PeterKnolle pointed out, there isn't a relationship.

Fortunately, there is a Force.com lab packages out there that can let you sync custom fields from opportunity line items to quote line items, we've been using it for a while without any major issues. It's rough if you look at the code, but considering the lack of maintenance it's very effective and bug free

share|improve this answer
    
If I remember correctly it doesn't support boolean fields out of the box. –  Girbot Aug 20 '14 at 12:58
    
I'll press on with creating my own triggers/code to sync the OLI and QLI as some of the reviews on the AppExchange weren't too positive about the product. As a developer, part of the fun is coming up with your own solution to a common problem. Should only be a day of messing about... –  Richard Durrant Aug 20 '14 at 19:57

QuoteLineItem does not have a direct reference back to OpportunityLineItem.

The Product and Schedule Objects ERD shows the relationships.

Products and Schedule Objects ERD

While both objects have a Lookup to PricebookEntry that does not relate them in a way that would allow you to get from QuoteLineItem to OpportunityLineItem, since on PricebookEntry could be related to multiple OrderLineItems (e.g., product added more than once, different unit prices).

Interestingly, the new OrderItem (Order Product) does have a Lookup to QuoteLineItem.

share|improve this answer
    
Thanks for you answer Peter - very interesting to see the OrderItem object. I would have enough information to associate each QLI with the OLI with the fields I have available - I will get coding tomorrow! –  Richard Durrant Aug 20 '14 at 19:54

After a quick hack this works. Next to refactor, error handle and tidy up...

trigger triggerQLI on QuoteLineItem (before insert) {

    //get the quote record and find the Oppty and match on the OLI that caused the QLI to be created. Set the QLI's course field accordingly...
    //match on product (pricebook?), price and quantity

    set<id> quoteIds = new set<id>();

    for(QuoteLineItem QLI : trigger.new){
        quoteIds.add(QLI.QuoteId);
    }

    map<id,Quote> quotes = new map<id,Quote>([select id, OpportunityId from Quote where id in:quoteIds]);

    set<id> opptyIds = new set<id>();
    for(Quote q : quotes.values()){
        opptyIds.add(q.OpportunityId);
    }

    map<id,Opportunity> opptys = new map<id,Opportunity>([select id, name, (select id, Course__c, OpportunityId,PricebookEntryId,Product2Id,Quantity,UnitPrice from OpportunityLineItems) from Opportunity where id in:opptyIds]);



    for(QuoteLineItem QLI : trigger.new){
        id opptyId = ((Quote)quotes.get(QLI.QuoteId)).OpportunityId;

        Opportunity oppty = opptys.get(opptyId);

        for(OpportunityLineItem OLI : oppty.OpportunityLineItems){
            if(OLI.Product2Id == QLI.Product2Id && OLI.Quantity == QLI.Quantity && QLI.UnitPrice == QLI.Unitprice){
                    //SET THE QLI FIELDS HERE AS NECESSARY
                    QLI.Course__c = OLI.Course__c;
                    break;
                }
        }

    }

}
share|improve this answer
    
Interestingly enough, you can add formula fields on QuoteLineItem that get their value from the related OpportunityLineItem. I just created a formula field on QuoteLineItem: OpportunityLineItem.GUID__c which did get the correct value! –  Lex Nov 10 '14 at 13:41
    
Lex according to the order of execution. The trigger is fired first then after the formula field get executed. So the trigger always find the formula's value is null. this is my problem. –  LoveLace Jun 15 at 11:05
    
Richard Durrant I'm using too Product2Id to map betwen the opp line items and quote line items but it seems these two objects don't have this field so I use the PriceBook.Product2Id but with the after insert trigger I couldnt get the PriceBookEntry.Product2Id of the inserting quote line item. any idea how can i fix that please? – –  LoveLace Jun 15 at 11:33
    
You will need to execute soql to get the data unless you have formula fields on the OLI/QLI if the Product2Id field isn't available. –  Richard Durrant Jun 15 at 14:56
    
Richard Durrant I did the same thing but the OLI is always null. It's like the code is operating in before insert of the OLI. Can you check my code please. –  LoveLace Jun 16 at 9:23

Here is my trigger:

trigger QuoteLineItemAfterInsert on QuoteLineItem (after  insert){

System.debug('###### QUOITE LINE ITEM AFTER INSERT   ######');


set<id> quoteIds = new set<id>();
set<id> opptyIds = new set<id>();

for(QuoteLineItem QLI : trigger.new){
    quoteIds.add(QLI.QuoteId);
}

map<id,Quote> quotes = new map<id,Quote>([select id, OpportunityId, isSyncing from Quote where isSyncing = true and id in:quoteIds]);


for(Quote q : quotes.values()){
    opptyIds.add(q.OpportunityId);
}
list<Opportunity> lstOpp= new list<Opportunity>();


lstOpp=[select id, name, (select PricebookEntry.Product2Id, Mode_de_financement__c, Duree_Produit_Service__c, OpportunityId from OpportunityLineItems) from Opportunity  where id in:opptyIds];
map<id,Opportunity> opptys = new map<id,Opportunity>(lstOpp);


list<OpportunityLineItem> lstUpdateOLI = new list<OpportunityLineItem> ();
list<OpportunityLineItem> lstOLI = new list<OpportunityLineItem> ();

list<QuoteLineItem> lstQLI = new list<QuoteLineItem> ();

Set<String> setOppIds = new Set<String>();

for(QuoteLineItem QLI : trigger.new){

    lstQLI.add(QLI);
 }
 Opportunity Opp= new Opportunity();
 QuoteLineItem QuLI= new QuoteLineItem();
 for (QuoteLineItem QLI : trigger.new){

    id opptyId = ((Quote)quotes.get(QLI.QuoteId)).OpportunityId;
    Opp=[select id, name, (select PricebookEntry.Product2Id, Mode_de_financement__c, Duree_Produit_Service__c, OpportunityId from OpportunityLineItems) from Opportunity  where id =:opptyId ];

    system.debug('####### LISTE QUOTE LINE ITEM --------' + lstQLI);

    for(OpportunityLineItem OLI : Opp.OpportunityLineItems){

        system.debug('##### OPP LINE ITEMS ------ '+Opp.OpportunityLineItems);

        QuLI= [select Id,PricebookEntry_Product2Id__c, OpportunityLineItemId__c from QuoteLineItem where Id=: QLI.Id];

        system.debug('#### OLI.PricebookEntry.Product2Id -----'+OLI.PricebookEntry.Product2Id );
        system.debug('#### QLI.PricebookEntry Product2 ----- '+ QuLI.PricebookEntry_Product2Id__c);


        if(OLI.PricebookEntry.Product2Id== QuLI.PricebookEntry_Product2Id__c){
            OLI.Duree_Produit_Service__c= QuLI.Duree_Produit_Service__c;
            OLI.Mode_de_financement__c= QuLI.Mode_de_financement__c ;
            }
         lstUpdateOLI.add(OLI);

           system.debug('##### lstUpdateOLI  -----' +lstUpdateOLI); 
    }
 }
update lstUpdateOLI;
}
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.