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
RaoSRaoS 

Method to update a record in future method

Hi Gurus,
I have a trigger which inturn calls a apex method to make a API call to the third party.  After the third party returns the response, I should he updating the values on the record which called the API.

I am able to capture the response but while updating the record I am getting the error.  I am sure, the way I am calling the update method is causing the issue.  

Here is the trigger code:
-------------------------------------------

trigger updateSEToTR on Service_Entity__c (after insert, after update) { 
    //instead of calling the method directly, first iterate over all records and collects Ids of all records to be processed.
    //synchSEDeptSitesToTR.syncSEToTR();
       List<Id> lstSEToUpdate=new List<Id>(); 
        system.debug('---Inside---');
        if((Trigger.isInsert && Trigger.isAfter) || (Trigger.isUpdate && Trigger.isAfter))
        //if((Trigger.isInsert && Trigger.isAfter))
        {
            for(Service_Entity__c se : (List<Service_Entity__c>)trigger.new)
            {   
                If (se.Sync_Status__c<>'Error' && se.Send_To_TR__c==True)
                {
                    lstSEToUpdate.add(se.Id);
                }
            }
        system.debug('---lstSEToUpdate.size()---'+lstSEToUpdate.size());    
        //if there are records to update, call future method for callout, future method can only take list of Ids and can't take objects. Also Trigger.New and Trigger.IsInsert etc will not work in future method.
         If (lstSEToUpdate.size()> 0){
            synchSEDeptSitesToTR.syncSEToTR(lstSEToUpdate);
            synchSEDeptSitesToTR.LockSERecord(lstSEToUpdate);
         }

    }system.debug('-At the End--lstSEToUpdate.size()---'+lstSEToUpdate.size());        
}
-------------------------------------------
Here is the Apex code:
--------------------------------------------
public class synchSEDeptSitesToTR {
   @Future(callout=true) //needed to change the annotation
    public static void syncSEToTR(List<String> serviceEntityIds) {
        String trCustomerCode,trCustomerId,syncStatus,syncErrorMessage;
        List<Id> lstServiceEntitiesToUpdate=new List<Id>();
        system.debug('--serviceEntityIds--'+serviceEntityIds);
        List<Service_Entity__c> recordsToProcess=[Select Name, Id,Sync_Status__c,Send_To_TR__c, TR_Customer_Code__c,TR_Customer_Id__c,GP_Customer_Accounting_Code__c,CurrencyISOCode,
                                                  AcquisitionDB__c,Customer_Name__c,Legal_Entity__c,Sub_Branch__c,Accountrep__c,
                                                  NextRateIncrease__c,LeadSource__c,Account_Industry__c,DiscontinueService__c,
                                                  Taxable__c,TaxCode__c,Delivery_Contact__c,Delivery_Address__c,Delivery_Address_2__c,
                                                  Delivery_Contact_Phone__c,Delivery_Contact_Email__c,Delivery_State__c,Delivery_City__c,
                                                  Delivery_Postal__c,Billing_Contact__c,Billing_Address__c,Billing_Address_2__c,Billing_Contact_Phone__c,
                                                  Billing_Contact_Email__c,Billing_State__c,Billing_City__c,Billing_Postal__c,PO_Number__c,
                                                  Billing_Group__c,Breach_Reporting_Services__c,Admin_Fee_V4__c,Online_Tools__c,Billing_Dept__c,
                                                  Information__c,Customer_Notes__c,Operations_Note__c,Display_as_a_pop_up_note__c,Conversion_Services__c,
                                                  HC_Storage__c,Shredding__c,Software__c,Media_Vault__c,AcctPrefixCode__c,TR_Account_Status__c,TR_NextRateIncrease__c,
                                                  Sub_Branch__r.Sub_Branch_Code__c
                                                  from Service_Entity__c where id in : serviceEntityIds and Send_To_TR__c=True and Sync_Status__c!='Error'];
        system.debug('--Before Try--');
        try{
        //get the records, added couple of more fields which are being used down
        system.debug('--Before For--');
        for (Service_Entity__c se1 : recordsToProcess)
        {
        system.debug('--Inside For--');
            string reqXML=createXMLforServiceEntity(se1);
            system.debug('--ServiceEntity--reqXML--' +reqXML);    
            
            HttpResponse response;
            //LockSERecord(recordsToProcess);
            if ((se1.TR_Customer_Id__c==null || se1.TR_Customer_Id__c=='0') && se1.Send_To_TR__c==True)
            { 
                system.debug('------before insertTR is called');
            response=insertTR(reqXML,'E'); 
                system.debug('------after insertTR is called');
            }
            else if ((se1.TR_Customer_Id__c<>null || se1.TR_Customer_Id__c<>'0') && se1.Send_To_TR__c==True)
            {
            system.debug('------before updateTR is called');    
            system.debug('--Userinfo.getName()---'+Userinfo.getName());    
            response=updateTR(reqXML,'E'); 
            system.debug('after updateTR is called');
            }    
            
            // Parse the JSON response
            // 400 bad request
            // 200 success
            String respText=response.getBody();
            if (response.getStatusCode() != 200) {
                syncStatus='Error';
                syncErrorMessage=respText.substringBetween('<ErrorMessage>','</ErrorMessage>');
                System.debug('The status code returned was not expected: ' +
                             response.getStatusCode() + ' ' + response.getStatus());
                system.debug('syncStatus:'+syncStatus); 
            } else {
                System.debug(response.getBody());
                if (response.getStatusCode() == 200){
                    system.debug('---' + response.getStatusCode() + ' ' + response.getStatus());
                    trCustomerCode=respText.substringBetween('<TRCustomerCode>', '</TRCustomerCode>');
                    trCustomerId=respText.substringBetween('<TRCustomerId>', '</TRCustomerId>');
                    syncErrorMessage='';
                    syncStatus='Synced';
                    system.debug('trCustomerCode:'+trCustomerCode);
                    system.debug(' trCustomerId:'+ trCustomerId);
                    system.debug('syncStatus:'+syncStatus); 
                    se1.TR_Customer_Code__c=trCustomerCode;
                    se1.TR_Customer_Id__c=trCustomerId;
                    se1.Sync_Status__c=syncStatus;
                    
                  //lstServiceEntitiesToUpdate.add(se1.Id);  
                }
                //Method to update this record with the values from the response
                updateSERecord(se1.Id,trCustomerCode,trCustomerId,syncStatus);
                
            }
            
        }

        }
        catch(Exception e){ 
            System.debug('Exception:'+e.getMessage());
        } 
          
         UnLockSERecord(recordsToProcess);
    }
    //method to get token
    public static string getToken()
    {
        Http http = new Http();
        HttpRequest request = new HttpRequest();
        request.setEndpoint(tokenUrl);
        request.setMethod('POST');
        request.setHeader('Content-Type', 'application/json;charset=UTF-8');
        String jsonBody ='username=' + 'user1' + '&password=' + '12345';
        request.setBody(jsonBody);
        request.setHeader('Content-Type', 'application/x-www-form-urlencoded;charset=UTF-8');
        HttpResponse response = http.send(request);  

        String respText=response.getBody();
        String respToken=respText.substringAfter('access_token');
        
        //System.debug('---respToken---' + respToken);
        String respToken1=respText.substringBetween('access_token', 'token_type');
        String respToken2=respToken1.replace('":"','');
        String respToken3=respToken2.replace('","','');
   
        System.debug('--respToken3--' +respToken3);  
        
        return respToken3;    
    }
    //new method to create XML for Service entity
    public static string createXMLforServiceEntity(Service_Entity__c se1){
        String ZeroOne='';
        string seReq='';
        seReq=seReq+'<ServiceEntity>';
        seReq=seReq+'<ServiceEntityID>' + se1.Id+'</ServiceEntityID>';
        seReq=seReq+'<ServiceEntityCode>' + se1.Name+'</ServiceEntityCode>';
        seReq=seReq+'<TRCustomerCode>' + se1.TR_Customer_Code__c+'</TRCustomerCode>';
        //seReq = seReq.replace('null','');
        seReq=seReq+'<TRCustomerId>' + se1.TR_Customer_Id__c+'</TRCustomerId>';
        seReq=seReq+'<CustomerName>' + se1.Customer_Name__c+'</CustomerName>';
        //seReq=seReq+'<SubBranch>' + se1.Sub_Branch__c+'</SubBranch>';
        //seReq = seReq.replace('null','');
        seReq=seReq+'<SubBranchCode>'+se1.Sub_Branch__r.Sub_Branch_Code__c+'</SubBranchCode>';
        If (se1.AcctPrefixCode__c<>'-')
        {
        seReq=seReq+'<AccountPrefixCode>' + se1.AcctPrefixCode__c+'</AccountPrefixCode>';
        }
        else
        {
        seReq=seReq+'<AccountPrefixCode>MA-8</AccountPrefixCode>';
        }    
        //seReq = seReq.replace('-','MA-8');
        seReq=seReq+'<GPCustomerAccountingCode>' + se1.GP_Customer_Accounting_Code__c+'</GPCustomerAccountingCode>';
        seReq=seReq+'<Currency>' + se1.CurrencyISOCode+'</Currency>';
        //List<acquisition__c> a; //acquisition__c a;
        List<acquisition__c> a=[select name from acquisition__c where id=:se1.AcquisitionDB__c Limit 1];        
        If (a.size()==0)
        {
        seReq=seReq+'<Acquisition></Acquisition>';     
        }
        else{
            seReq=seReq+'<Acquisition>' + a[0].Name+'</Acquisition>';}
  
        string LineOfBusiness='';
               
        seReq=seReq+'<LinesofBusiness>' + LineOfBusiness+'</LinesofBusiness>';
        seReq=seReq+'<LegalEntity>' + se1.Legal_Entity__c+'</LegalEntity>';
        
        List<User> c3=[select name from User where id=:se1.Accountrep__c Limit 1];

        If (c3.size()==0){
        seReq=seReq+'<AccountRep></AccountRep>';
        }
        Else
        {seReq=seReq+'<AccountRep>' + c3[0].Name+'</AccountRep>';
        }
        //seReq=seReq+'<AccountRep>' + se1.Accountrep__c+'</AccountRep>';
        seReq=seReq+'<AccountStatus>' +se1.TR_Account_Status__c+'</AccountStatus>';

        seReq=seReq+'</ServiceEntity>';    
        seReq = seReq.replace('null',''); 
        return seReq;
    }
        //method to make the call to TR and submit the request
    public static httpresponse insertTR(String Reqxml, String Type)
    {
              String token=getToken();  
            system.debug('token---'+token);
            String header1='Bearer '+token;
            Http http1 = new Http();
            HttpRequest request1 = new HttpRequest();
        If (Type=='E')
        {system.debug('before create Account///');
            request1.setEndpoint(url1);
        system.debug('after create Account///');}
        else If (Type=='D')
                    {system.debug('before create Department////');
            request1.setEndpoint(url12);
        system.debug('after create Department////');}
         else If (Type=='S')        {system.debug('before create Site///');
            request1.setEndpoint(url13);
        system.debug('after create Site///');}
            request1.setMethod('POST');
            //request1.setHeader('Content-Type', 'application/json;charset=UTF-8');
            
            request1.setHeader('Authorization',header1);
            request1.setHeader('Accept', 'application/xml');
            request1.setHeader('Content-Type', 'application/xml');
            
            //string reqXML=createXMLforServiceEntity(se1);
            system.debug('---reqXML--' +ReqXML);
            request1.setBody(ReqXML);
            request1.setTimeout(60000);
            If (test.isRunningTest())
            {
                //write the code for response
                
            }
        Else{//once u generate the fake response uncomment the 238 and comment 240
            //HttpResponse response1 = http1.send(request1);  
            }   
            HttpResponse response1 = http1.send(request1);
        
            System.debug('--response1.getStatusCode() + -- + response1.getStatus()'+response1.getStatusCode() + ' ' + response1.getStatus());
            System.debug('---response1.getBody--' +response1.getBody());
            return response1;
    }
    public static httpresponse updateTR(String Reqxml,String Type)
    {
              String token=getToken();  
            //system.debug('token---'+token);
            String header1='Bearer '+token;
            Http http1 = new Http();
            HttpRequest request1 = new HttpRequest();
        If (Type=='E')
        {   system.debug('before update Account'); 
            request1.setEndpoint(url14);//?_HttpMethod=PATCH');
        system.debug('after update Account');}
        else If (Type=='D')
                    {system.debug('before update Department');
            request1.setEndpoint(url15);
        system.debug('after update Department');}
         else If (Type=='S')        {system.debug('before update Site');
            request1.setEndpoint(url16);
        system.debug('after update Site');}    
        //request1.setHeader('X-HTTP-Method-Override','PATCH');
        request1.setMethod('PUT');
       //request1.setMethod('PATCH');
            //request1.setHeader('Content-Type', 'application/json;charset=UTF-8');
            
            request1.setHeader('Authorization',header1);
            request1.setHeader('Accept', 'application/xml');
            request1.setHeader('Content-Type', 'application/xml');
            
            //string reqXML=createXMLforServiceEntity(se1);
            system.debug('---reqXML--' +ReqXML);
            request1.setBody(ReqXML);
            //request1.setTimeout(10000);
            HttpResponse response1 = http1.send(request1);  
            System.debug('--response1.getStatusCode() + -- + response1.getStatus()'+response1.getStatusCode() + ' ' + response1.getStatus());
            System.debug('---response1.getBody--' +response1.getBody());
            return response1;
    }
    //Lock the submitted record so that no one can edit.
    public static void LockSERecord(List<Id> recordsToProcess1)
    {
                //Lock the record from editing
        Approval.LockResult[] lrList = Approval.lock(recordsToProcess1, false);
        for(Approval.LockResult lr : lrList) { 

           if (lr.isSuccess()) 
        { 
         // Operation was successful, so get the ID of the record that was processed 
         System.debug('Successfully locked SE with ID: ' + lr.getId()); 
           } 
           else { 
         // Operation failed, so get all errors         
         for(Database.Error err : lr.getErrors()) { 
               System.debug('The following error has occurred.');           
               System.debug(err.getStatusCode() + ': ' + err.getMessage()); 
               System.debug('SE fields that affected this error: ' + err.getFields()); 
             } //for end
           } // else end
         } // main for end
        
        //End Locking process
    }
//unLock the submitted record after successful response code.
    public static void UnLockSERecord(List<Service_Entity__c> recordsToProcess1)
    {
                //Lock the record from editing
        Approval.unLockResult[] lrList = Approval.unlock(recordsToProcess1, false);
        for(Approval.unLockResult lr : lrList) { 

           if (lr.isSuccess()) 
        { 
         // Operation was successful, so get the ID of the record that was processed 
         System.debug('Successfully unlocked SE with ID: ' + lr.getId()); 
           } 
           else { 
         // Operation failed, so get all errors         
         for(Database.Error err : lr.getErrors()) { 
               System.debug('The following error has occurred.');           
               System.debug(err.getStatusCode() + ': ' + err.getMessage()); 
               System.debug('SE fields that affected this error: ' + err.getFields()); 
             } //for end
           } // else end
         } // main for end
        
        //End unLocking process
    }
    
    //method to update the SE record that got the response from API call
    @future
    public static void updateSERecord(Id ids,string trCustCode, string trCustId, string syncstatus)
    {
      List<Service_Entity__c> se2=[Select id from Service_Entity__c where id = :ids];  
      for (Service_Entity__c se3 : se2 )
        {  
      se3.TR_Customer_Code__c=trCustCode;
      se3.TR_Customer_Id__c=trCustId;
      se3.Sync_Status__c=syncstatus;
      update se3;      
        }
        
    }

--------------------------------------------
Here is the error i am getting
---------------------------------------------
Future method cannot be called from a future or batch method: synchSEDeptSitesToTR.syncSEToTR(List<String>)
---------------------------------------------

Kindly guide me in fixing this issue.

Thanks
Rao
Best Answer chosen by RaoS
Ravi Dutt SharmaRavi Dutt Sharma
Remove the @future annotation from updateSERecord method. You do not need this annotation because your calling method is already executing in a future context.
public static void updateSERecord(Id ids,string trCustCode, string trCustId, string syncstatus)
    {
      List<Service_Entity__c> se2=[Select id from Service_Entity__c where id = :ids];  
      for (Service_Entity__c se3 : se2 )
        {  
      se3.TR_Customer_Code__c=trCustCode;
      se3.TR_Customer_Id__c=trCustId;
      se3.Sync_Status__c=syncstatus;
      update se3;      
        }
        
    }

All Answers

Ravi Dutt SharmaRavi Dutt Sharma
Remove the @future annotation from updateSERecord method. You do not need this annotation because your calling method is already executing in a future context.
public static void updateSERecord(Id ids,string trCustCode, string trCustId, string syncstatus)
    {
      List<Service_Entity__c> se2=[Select id from Service_Entity__c where id = :ids];  
      for (Service_Entity__c se3 : se2 )
        {  
      se3.TR_Customer_Code__c=trCustCode;
      se3.TR_Customer_Id__c=trCustId;
      se3.Sync_Status__c=syncstatus;
      update se3;      
        }
        
    }
This was selected as the best answer
Ajay K DubediAjay K Dubedi
Hi RaoS,

As the error states you are calling a future method from another future method.
Hence would suggest to keep only one class and do all future operations in a single future class
OR 
implements Database.AllowCallout in your synchSEDeptSitesToTR  class

Database.AllowCallout helps in integrate salesforce with the external server by making a call to an external web service.

I hope you find the above solution helpful. If it does, please mark as Best Answer to help others too.
Thanks,
Ajay Dubedi
RaoSRaoS
Thank you Ravi and Ajay for your input.

I will modify my code and test it and let you all know.

Thanks
Rao.
RaoSRaoS
Thank you guys for your help. I have removed @future for the update method.

I have used another variable to bypass the trigger when I am making the update call.
public static void updateSERecord(Id ids,string trCustCode, string trCustId, string syncstatus)
{
List<Service_Entity__c> se2=[Select id from Service_Entity__c where id = :ids];
for (Service_Entity__c se3 : se2 )
{
se3.TR_Customer_Code__c=trCustCode;
se3.TR_Customer_Id__c=trCustId;
se3.Sync_Status__c=syncstatus;
ByPassTrigger=true;
update se3;
}
}

Modified the trigger logic to run the trigger when this variable is true by this additional statement check
if (!ByPassTrigger) then execute the trigger.