Friday 26 September 2014

How to Automatically Sync new Quote with Opportunity in Apex Trigger

Here is the way to Automatically Sync new Quote with Opportunity in Apex Trigger. Before that some of the information about Quote and Opportunity Field Details that are,

In Quote object there is a field called IsSyncing, This is a Read Only field in salesforce. If you run the below snippet in developer console the field result such as isCreatable, isUpdatable is false. So you can’t create/update this field value.

1.    Schema.DescribeFieldResult dfr = Schema.sObjectType.Quote.fields.IsSyncing;
2.    System.debug(dfr);

In Opportunity object there is a field called SyncedQuoteID, If you run the below snippet in developer console the field result such as isCreatable, isUpdatable is True. I.e. you can do insert and update on this field.

1.    Schema.DescribeFieldResult dfr = Schema.sObjectType.Opportunity.fields.SyncedQuoteId;
2.    System.debug(dfr);

But if you update the Opportunity field SyncedQuoteID in a trigger, you will get the below error.

“The opportunity SyncedQuote field is read only within a trigger”

For security reason by default in salesforce trigger you can’t update the SyncedQuoteID field even if you move the update coding in to separate class.

To overcome this problem you need to go for @future, By using this it will go with the separate transaction and the DML operation allowed. The below code snippet will useful to this problem.


Apex Trigger



  1. trigger QuoteAutoSync on Quote (after insert)
  2. {
  3.     Map<Id, Id> quoteMap = new Map<Id, Id>();
  4.     for(Quote currentQuote : Trigger.New)
  5.     {
  6.         quoteMap.put(currentQuote.Id, currentQuote.OpportunityId);
  7.     }
  8.     QuoteAutoSyncUtil.syncQuote(quoteMap);
  9. }

Apex Class


  1. public class QuoteAutoSyncUtil
  2. {
  3.     @future
  4.     public static void syncQuote(Map<Id, Id> quoteMap)
  5.     {
  6.         Map<Id, Opportunity> oppMap = new Map<Id, Opportunity>();
  7.         for(Id currentQuote : quoteMap.keyset())
  8.         {
  9.             Opportunity opp = new Opportunity();
  10.             opp.Id = quoteMap.get(currentQuote);
  11.             opp.SyncedQuoteId = currentQuote;
  12.             oppMap.put(opp.Id, opp);
  13.         }
  14.         update oppMap.values();
  15.     }
  16. }

Test Class


  1. @isTest
  2. private class TestQuoteAutoSync
  3. {
  4.     static testMethod void insertQuote()
  5.     {
  6.         Opportunity opp = new Opportunity();
  7.         opp.Name = 'Test Opportunity';
  8.         opp.StageName = 'Prospecting';
  9.         opp.CloseDate = system.today();
  10.         insert opp;
  11.        
  12.         Quote quo = new Quote();
  13.         quo.Name = 'Test Quote';
  14.         quo.OpportunityId = opp.Id;        
  15.        
  16.         Test.startTest();
  17.         insert quo;
  18.         Test.stopTest();
  19.        
  20.         Opportunity o = [select SyncedQuoteId from opportunity where id=:opp.Id];
  21.         system.assert(o.SyncedQuoteId != null);
  22.                
  23.     }
  24. }

18 comments:

  1. Great. Thanks for this information

    ReplyDelete
  2. This works great but I am having trouble writing an associated test class(es) to go along with it. Can you help? This is what I have so far:

    @isTest
    public class AutoSyncQuote {

    public static testmethod void TestAutoSyncQuote(){
    Account a = new Account();
    a.Name = 'Test Account';
    a.salesReach__Agent_Account_Company_Name__c = '001a000001DbY9F';
    a.salesReach__Agent_Contact_Name__c = '003a000001iM38p';
    a.Account_Status__c = 'Active';
    a.Diamond_Account__c = True;
    a.Net_One__c = False;

    Insert a;

    Opportunity opp = new Opportunity();
    opp.Name = 'Telecom Product or Service';
    opp.CloseDate = Date.today();
    opp.StageName = 'Quote Requested By Client';
    opp.Opportunity_Type__c = 'Install New Service';
    opp.AccountId = a.Id;

    Insert opp;

    Quote q = new Quote();
    q.Name = 'Telecom Product or Service - Quote';
    q.ExpirationDate = opp.Quote_Expiration_Date__c;


    }

    }

    ReplyDelete
  3. Hi DaNe, I have updated this post with test class. Hope this will solve your problem.

    ReplyDelete
  4. Works great! Thank you!!!

    ReplyDelete
  5. Do you have one that is similar for Opportunity Line Item with Quote Line Item?

    ReplyDelete
  6. Still I am getting this error
    Oppty_renewal: execution of BeforeUpdate caused by: System.UnexpectedException: The opportunity SyncedQuote field is read only within a trigger.:

    ReplyDelete
  7. Nice to be visiting your blog again, it has been months for me. Well this article that i've been waited for so long. I need this article to complete my assignment in the college, and it has same topic with your article. Thanks, great share. urdughr

    ReplyDelete
  8. https://dimpoetry.com/sad-poetry-sad-poetry-in-urdu-sad-poetry-images/

    ReplyDelete
  9. THANK YOU SO MUCH FOR YOUR INFORMATION AND GUIDE AND HELP. I REALLY ENJOY THIS CONTENT. I HOPE YOU PROVIDE US AGAIN SAME THING.
    UrduSadPoetry

    ReplyDelete
  10. Thanks for you kind information and help. This is really a interesting information regarding the topic if you want to you know about more check below.
    Love Poetry In Urdu
    Khalil Ur Rehman Qamar Poetry
    Attitude Poetry In Urdu

    ReplyDelete
  11. THANK YOU SO MUCH FOR YOUR INFORMATION AND GUIDE AND HELP. I REALLY ENJOY THIS CONTENT. I HOPE YOU PROVIDE US AGAIN SAME THING.
    Urdu Jokes

    ReplyDelete
  12. Thank you so much for such a great information. Sad Punjabi Status

    ReplyDelete
  13. This comment has been removed by the author.

    ReplyDelete
  14. Its a very informative blog and helpful Desi status in punjabi

    ReplyDelete

Activities: Assign Tasks to a Queue Salesforce Lightning

Salesforce announced to assign Tasks to a Queue beginning from Spring'20 release. How does it work? In Setup, enter Queues in th...