Some time we need to sync Opportunity Line Item and Quote Line Item.When we create Quote automatically Opportunity line item created as Quote Line Item. But Some time we also need to sync custom field.
Requirement :-
I would like to sync between opportunity line item and quote line item. I have a few custom fields defined in addition to standard fields in both quote line item and opportunity line item objects. Custom fields in both objects are exact replica.
Solution :-
For above requirement I have created the below trigger :-
/*
***********************************************************************
Created By :- Amit Chaudhary
Created Date :- 06 NOV 2014
Desc :- Syncing custom fields between quotes and opportunities
***********************************************************************
*/
trigger QuoteLineItemTrigger on QuoteLineItem (after insert){
map<string,string> mapQuoteOppty=new map<string,string>();
string JSONContent=Json.Serialize(Trigger.New);
JSONParser parser =JSON.createParser(JSONContent);
list<string> OpptyLineId=new list<string>();
list<string> QuoteLineId=new list<string>();
System.debug('parser-------->'+parser );
while (parser.nextToken() != null)
{
if(parser.getCurrentToken()==JSONToken.VALUE_STRING && parser.getCurrentName()=='OpportunityLineItemId')
OpptyLineId.add(parser.getText());
if(parser.getCurrentToken()==JSONToken.VALUE_STRING && parser.getCurrentName()=='Id')
QuoteLineId.add(parser.getText());
parser.nextToken();
if(parser.getCurrentToken()==JSONToken.VALUE_STRING && parser.getCurrentName()=='OpportunityLineItemId')
OpptyLineId.add(parser.getText());
if(parser.getCurrentToken()==JSONToken.VALUE_STRING && parser.getCurrentName()=='Id')
QuoteLineId.add(parser.getText());
}
System.debug('OpptyLineId-------->'+OpptyLineId);
System.debug('QuoteLineId-------->'+QuoteLineId);
integer iCount=0;
for(string strOppLineId : OpptyLineId)
{
string iQuoteLineId=QuoteLineId[iCount];
mapQuoteOppty.put(iQuoteLineId,strOppLineId);
iCount++;
}
Set<Id> SetOppId=new Set<Id>();
Set<Id> SetQuoteId=new Set<Id>();
for(QuoteLineItem QLI:trigger.new)
{
SetQuoteId.add(QLI.QuoteId);
}
List<Quote> Lstquotes =[select id, OpportunityId, isSyncing from Quote where Id in :SetQuoteId];
for(Quote Qt:Lstquotes)
{
SetOppId.add(Qt.OpportunityId);
}
List<OpportunityLineItem> lstOLI=[select Id, OpportunityId,Negotiated__c,End_Date__c,Start_Date__c from OpportunityLineItem where OpportunityId in:SetOppId];
Map<Id,OpportunityLineItem> MapOLI=new Map<Id,OpportunityLineItem>([select Id,Negotiated__c, OpportunityId, End_Date__c,Start_Date__c from OpportunityLineItem where OpportunityId in:SetOppId]);
Map<Id,QuoteLineItem > MapQLI=new map<Id,QuoteLineItem>([Select Id, Negotiated__c, End_Date__c,Start_Date__c from QuoteLineItem where QuoteId in:SetQuoteId]);
list<QuoteLineItem> updateQuoteLineItem =new list<QuoteLineItem >();
for(QuoteLineItem qli:MapQLI.values())
{
System.debug('&&&&'+mapQuoteOppty);
if(mapQuoteOppty.get(qli.id)!=null)
{
String OppID = mapQuoteOppty.get(qli.id);
OpportunityLineItem OLI = MapOLI.get(OppID);
qli.End_Date__c=OLI.End_Date__c;
qli.Start_Date__c=OLI.Start_Date__c;
qli.Negotiated__c= OLI.Negotiated__c;
updateQuoteLineItem.add(qli);
}
}
update updateQuoteLineItem;
}
Related Link :-
Check this in AppExchange
https://sites.secure.force.com/appexchange/listingDetail?listingId=a0N30000003IlfVEAS
Custom Quote Sync
App by Force.com Labs 11/2/2010
https://sites.secure.force.com/appexchange/listingDetail?listingId=a0N30000003IlfVEAS
Custom Quote Sync
App by Force.com Labs 11/2/2010
Thanks
Amit Chaudhary
Nice post
ReplyDeleteThanks
DeleteVery good solution ...Tricky one :)
ReplyDeleteThanks Rajeev
DeleteThis code needs to paste in which object? opportunity or quote
ReplyDeleteI created a trigger on QuoteLineItem object. You can also create one button on Quote to Sync and use above code
ReplyDeleteIt looks like you can achieve something similar using a formula field. You won't be able to create it using the wizard, but you can manually enter the "OpportunityLineItem.Id" as the formula, for a formula field on QuoteLineItem.
ReplyDeleteOriginal source: Lex's comment on http://salesforce.stackexchange.com/questions/47343/does-quote-line-item-reference-back-to-which-oli-it-relates-to.
Good Catch. But still to syn QuoteLineIteam and OpportunityLineItem you need to write Trigger or apex code.
DeleteThat is very strange.. I created this FF and it works nice, but no idea how it works! As per data model, there isn't a way you can do this traversal and there is many to many involved. Any idea HOW it works?
DeleteYes, With the formula field you will get the Opportunity line item ID on Quote line item. After that to Sync Opportunity Line Item with Quote Line Item you can create a trigger Quote line item.
DeleteAbove Trigger will help you Pull the custom field values on Quote line Item. If you want to pull the values from Quote line item to Opportunity line Item then you need to create one more trigger to Sync Quote Line Item with Opportunity line Item.
Thank
Amit Chaudhary
But how can I get the OpportunityLineItemId with formula field?
DeleteGo to Quote line Item and type OpportunityLineItemId directly
DeleteBut this field OpportunityLineItemId doesnt exist on QuoteLineItem . I am creating the formula on quoteLineItem
DeleteThis is the only tick here .Just create one formula field -> Then select text -> the type below formula :-
DeleteOpportunityLineItem.Id
If you need any help then please let me know
Thanks
Amit Chaudhary
amit.salesforce21@gmai.com
Hello Amit, I sent you an email. Check your inbox
DeleteNice Post Amit. This Trigger saved my day
ReplyDeleteThanks for this: I wondered What is the benefit of this trigger compared with the custom sync app from Force.com?
ReplyDeleteSome time we need to sync Opportunity Line Item and Quote Line Item. For same we have Sync button on Quote. But with that button we can sync only Salesforce standard fields. Some time we also need to sync custom fields. In that case we need to create trigger for same.
DeleteIn App Exchange we have one app also for same. but that is manage package. you will not able to see code. But according to above trigger you can also add your own logic also.
DeleteCheck this in AppExchange
https://sites.secure.force.com/appexchange/listingDetail?listingId=a0N30000003IlfVEAS
This link is showing down for maintenance.
DeleteHow can you replicate this in Testing? Without a way to simulate the QLI creation from the OpptyLineItems, you lose the linkage.
ReplyDeleteRight..
ReplyDeleteFor same you can maintain the opportunity line item ID on QLI with the help of trigger.
Here's my problem and maybe you can give me some ideas... I created the Formula Field named RelatedOpptyLineItem using your idea, setting the formula to OpptorunityLineItem.id. So, in the real world events, when the user creates a quote from an Oppty with OpptyLineItems, that field is populated for the QLIs created from the OLIs. So my syncing trigger can easily match the QLI with the OLI.
DeleteHowever, this linkage is never created, since you can't (AFAIK) simulate this auto creation of QLIs in Apex.
My only thought, would be to add an additional field on the QLI which I can populate in my test environment, and add a few isTest clauses in my trigger, to use that field.
If you had any other ideas, I open for it. Thanks!
Yes. When ever you will create QLI from OLI ( Opportunity line Item ) then you will always get OLI Id in formula field.
DeleteI am not able to understand your 2nd point auto creation of QLI in APEX. If i am not wrong then you are creating QLI directly by Apex code ( you are not using standard button) . If Yes then you will not get Opportunity line item ID in formula field. In that case you need to create one text(18) field on QLI and need to populate opportunity line item ID in QLI custom field.
If you are using any code then please provide your code so that i can help you.
Thanks,
Amit Chaudhary
Email :- amit.salesforce21@gmail.com
I think I found a decent workaround...and it sounds like your suggestion. Interestingly, this can't be done in a Field Update WF. So, I still have my formula field to grab the related OpptyLineItem on my QLI. I also created another field on the QLI, named RelatedOpptyLineItemID. I added a before insert trigger on the QLI, to populate the new text field RelatedOpptyLineItemID with the value in the formula field RelatedOpptyLineItem.
DeleteMy sync trigger now exclusively uses the new field for matching, and in my test code I can populate that field when I create the QLI.
for(QuoteLineItem qli : newQLIs){
//Could not do this in a WorkFlow
if(qli.RelatedOpptyLineItem__c!=null){
qli.RelatedOpptyLineItemID__c=qli.RelatedOpptyLineItem__c;
}
}
Ohh Great.
DeleteI hope your problem is resolved with new custom field. Please let me know if are still facing this issue.
Nice post..
ReplyDeleteThanks for Feedback
DeleteExcellent post Amit. Thanks
ReplyDeleteThanks for your feedback
DeleteThis comment has been removed by the author.
ReplyDeleteReally helpful. Can you please post test class for this?
ReplyDeleteExcellent Post - thanks for your help :)
ReplyDeleteI'm trying to sync a custom field on my Quote Line Item called "License Start Date" and sync it to the Opportunity Product Line Item. Would this work for me and where would I implement it?
ReplyDeleteIn case of field update at any of these fields, which we have synced , Will it work
ReplyDeletehi i want to populate my QLI id to the Description field of OLI can you please tell me how to do it
ReplyDeleteWonderful article Amit. However would really like to know your thoughts if its a good approach to update quote line item object in "after insert" trigger context.
ReplyDeleteThis comment has been removed by the author.
ReplyDelete