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
Baz DensonBaz Denson 

Queueing Webservice Callouts

My Process is as follows:

1. Process Builder: Opportunity reaches a certain stage and fires an invokable apex
2. Apex: The invokable apex uses @future (callout=true) to make a webservice call to our accounting software to create a customer in the accounting software.
3. Apex: When the webservice is finished, it creates a custom object record related to the account with the accounting software customer id.
4. Process Builder: when the custom object record is created it fires a second invokable apex  @future (callout=true) to make a webservice call to our accounting software to create an invoice in the accounting software, passing the customer id from stage 3.

The problem is I am running into is I am getting System.AsyncException: Future method cannot be called from a future or batch method errors when the second webservice is called.

My apex is as follows:

webservice 1:
 
/**
* @author       Barry Denson (barry.denson@hotmail.com)
* @description  Invocable method to create a Xero contact from SFDC
**/


global class SFtoXero{

    public static string ContactID;
    public static String InvoiceID;
    
    
    global class contactRecord {
        @invocableVariable global string PracticeName;
        @invocableVariable global string PartnerFirstName;
        @invocableVariable global string PartnerLastName;
        @invocableVariable global string EmailAddress;
        @invocableVariable global string AddressLine1;
        @invocableVariable global string AddressLine2;
        @invocableVariable global string AddressLine3;
        @invocableVariable global string AddressLine4;
        @invocableVariable global string City;
        @invocableVariable global string Region;
        @invocableVariable global string PostalCode;
        @invocableVariable global string Country;
        @invocableVariable global string AttentionOf;
        @invocableVariable global string AccountID;
        
    }
      
    @InvocableMethod(label='Create a Contact In Xero' description='Creates a Xero Contact Record')

    global static void createContact(List<contactRecord> XeroContacts) {
        
        for(contactRecord c: XeroContacts) {
            
            calloutMethod(c.PracticeName, c.PartnerFirstName, c.PartnerLastName, c.emailAddress, c.AddressLine1,c.AddressLine2,c.AddressLine3,c.AddressLine4, c.city, c.region, c.postalCode, c.country, c.attentionOf, c.AccountID);

        }
        
        
    
    }
    
    @future(callout=true)
    public static void calloutmethod(   string practiceName, 
                                        string partnerFirstname, 
                                        string partnerLastname,
                                        string emailAddress,
                                        string addressLine1,
                                        string addressLine2,
                                        string addressLine3,
                                        string addressLine4,
                                        string City,
                                        string region,
                                        string postalCode,
                                        string country,
                                        string attention,
                                        String AccountID){

        // Create a Contact
        // 

            XeroAddress addr = new XeroAddress();
            
            addr.AddressType ='POBOX';
            addr.AddressLine1 = addressLine1;
            addr.AddressLine2 = addressLine2;
            addr.AddressLine3 = addressLine3;
            addr.AddressLine4 = addressLine4;
            addr.City = City;
            addr.Region = region;                               
            addr.PostalCode = postalCode;
            addr.Country = country;
            addr.AttentionTo = attention;
                                            
            XeroContact newCon = new XeroContact();
                                            
            newCon.Name = practiceName;
            newCon.FirstName = partnerFirstname;
            newCon.LastName = partnerLastname;
            newCon.EmailAddress = emailAddress;
                                                          
            newCon.Addresses = new List<XeroAddress>() ;                              
                              
            newCon.Addresses.add(addr);  
                                            
            system.debug('Addresse: '+newCon);                                
            
        
        // Send Contact to Xero
            try {
                XeroContact createdContact=XeroAccountingApi.createContact(XeroXmlUtility.serialize(newCon, 'Contact'));
                ContactID = createdContact.ContactID;
                system.debug('Created Contact ID: '+createdContact.ContactID);
                system.debug('Stored Contact ID: '+ContactID);
                
                Xero_contact__c XeroContactinSF = new Xero_Contact__c();
                XeroContactinSF.Xero_Contact_ID__c = ContactID;
                XeroContactinSF.Name = PracticeName;
                XeroContactinSF.Account__c = AccountID;
                
                Account Acc = New Account();
                Acc = [select id, Xero_Contact_id__c from Account where Id = :AccountID];
                Acc.Xero_Contact_ID__c = ContactID;
                update Acc;
                
                //Xero_contact__c existing = [select Id from Xero_contact__c where Xero_Contact_ID__c = :createdContact.ContactID];
                //system.debug('Existing:'+existing);
                //if (existing.Id != null) XeroContactInSF.Id = existing.Id;
                
                upsert XeroContactinSF;
              
            } catch (Exception ex){
                system.debug('Contact:'+ex);
            }    
                           
    }

   
}

Webservice 2:
 
/**
* @author       Barry Denson (barry.denson@hotmail.com)
* @description  Invocable method to create a Xero contact from SFDC
**/

global class CreateNominalInvoice {


    global class InvoiceRecord { 
        @invocableVariable global  string AccountID;
        @invocableVariable global  string XeroContactID;
        @invocableVariable global  string PracticeName;
    }
    @InvocableMethod(label='Create a Nominal Invoice In Xero' description='Creates a Xero Invoice Record')  
    global static void createInvoice(List<InvoiceRecord> Inv) {
		 for(InvoiceRecord I: Inv) {
             
        
             calloutMethod(I.AccountID, I.XeroContactId, I.PracticeName  );

         }
     
    }
    
    @future(callout=true)
    public static void calloutmethod(   String AccountID, String XeroContactId, string practiceName){    
        /*
     * POST Invoices
     * Use this method to create or update an invoice
     * 
     * The following are required to create a draft 
     * 
     * invoice	Type	See Invoice Types	
     * Contact	See Contacts	
     * LineItems	See LineItems. The LineItems collection can contain any number of individual LineItem sub-elements. At least one is required to create a complete Invoice.	
     * 
     * The following are optional when creating or updating invoices	
     * Date	Date invoice was issued - YYYY-MM-DD. If the Date element is not specified it will default to the current date based on the timezone setting of the organisation	
     * DueDate	Date invoice is due - YYYY-MM-DD	LineAmountTypes	Line amounts are exclusive of tax by default if you don't specify this element. See Line Amount Types	
     * InvoiceNumber	
     * 	ACCREC - Unique alpha numeric code identifying invoice (when missing will auto-generate from your Organisation Invoice Settings) (max length = 255)	
     * 	ACCPAY - non-unique alpha numeric code identifying invoice. This value will also display as Reference in the UI. (max length = 255)	Reference	
     * 	ACCREC only - additional reference number (max length = 255)	
     * BrandingThemeID	See BrandingThemes	
     * Url	URL link to a source document - shown as "Go to [appName]" in the Xero app	
     * CurrencyCode	The currency that invoice has been raised in (see Currencies)	
     * CurrencyRate	The currency rate for a multicurrency invoice. If no rate is specified, the XE.com day rate is used. (max length = [18].[6])	
     * Status	See Invoice Status Codes	
     * SentToContact	Boolean to set whether the invoice in the Xero app should be marked as "sent". This can be set only on invoices that have been approved	
     * ExpectedPaymentDate	Shown on sales invoices (Accounts Receivable) when this has been set	
     * PlannedPaymentDate	Shown on bills (Accounts Payable) when this has been set	
     * 
     * Elements for LineItems	
     * 
     * Description	Description needs to be at least 1 char long. A line item with just a description (i.e no unit amount or quantity) can be created by specifying just a Description element that contains at least 1 character (max length = 4000)	
     * Quantity	LineItem Quantity (max length = 13)	
     * UnitAmount	Lineitem unit amount. By default, unit amount will be rounded to two decimal places. You can opt in to use four decimal places by adding the querystring parameter unitdp=4 to your query. See the Rounding in Xero guide for more information.	
     * ItemCode	See Items	
     * AccountCode	See Accounts	
     * LineItemID	The Xero generated identifier for a LineItem. It is recommended that you include LineItemIDs on update requests. If LineItemIDs are not included with line items in an update request then the line items are deleted and recreated.	
     * TaxType	Used as an override if the default Tax Code for the selected AccountCode is not correct - see TaxTypes.	
     * TaxAmount	The tax amount is auto calculated as a percentage of the line amount (see below) based on the tax rate. This value can be overriden if the calculated TaxAmount is not correct.	
     * LineAmount	The line amount reflects the discounted price if a DiscountRate has been used i.e LineAmount = Quantity * Unit Amount * ((100 - DiscountRate)/100) (can't exceed 9,999,999,999.99 )	
     * DiscountRate	Percentage discount being applied to a line item (only supported on ACCREC invoices - ACC PAY invoices and credit notes in Xero do not support discounts	
     * Tracking	Section for optional Tracking Category - see TrackingCategory. Any LineItem can have a maximum of 2 TrackingCategory elements.	
     * 
     * Elements for TrackingCategory
     * 	
     * Name	Name of the tracking category (required)	
     * OptionName	Name of the option (required)
    */
        
    
                        
                    XeroInvoice.LineItem LI = new XeroInvoice.LineItem();
                    LI.Description = 'Nominal Amount';
                    LI.Quantity = 1;
                    LI.UnitAmount = 1;
                        
                    XeroContact Co = New XeroContact();
                    co.ContactID = XeroContactID;  
                        
                    XeroInvoice newInvoice = new XeroInvoice();
                    newInvoice.Date_x = Datetime.now().format('yyyy-MM-dd')+'T00:00:00';
                    // Add additional Invoice details based on the XeroInvoice wrapper
    
                    newInvoice.Lineitems = new List<XeroInvoice.LineItem>() ;                              
                                  
                    newInvoice.Lineitems.add(LI);      
    
                    newinvoice.Contact = Co;
                    newInvoice.SentToContact = TRUE;
                    newInvoice.Status = 'AUTHORISED';
                    newInvoice.Type = 'ACCREC';
                                
                    // Send Invoice to Xero
                        try {
                            XeroAccountingApi.createInvoice(XeroXmlUtility.serialize(newInvoice, 'Invoice'));
                            
                            Xero_Invoice__c XeroInvoiceInSF = new Xero_invoice__c();
                            XeroInvoiceInSF.name = PracticeName;
                            XeroInvoiceInSF.Account__c = AccountID;
                            XeroInvoiceInSF.Xero_contact_id__c = XeroContactID;
                            
                            XeroInvoiceInSF.Invoice_Amount__c = 1.00;
                            XeroInvoiceInSF.Invoice_Date__c = system.today();
                            XeroInvoiceInSF.Xero_Invoice_Id__c = NewInvoice.InvoiceId;
                            Insert XeroInvoiceInSF;    
                       
                        }
                        catch (Exception ex) {
                            system.debug('Invoice:'+ex);    
                        }
    
            
    }   
}


How can I get the second call to only run once the first call is complete? Do I used scheduled apex, queuable?

Can you show me how to amend my code to acomplish this?

TIA

Barry