• Baz Denson
  • NEWBIE
  • 65 Points
  • Member since 2017
  • Salesforce Solution Engineer
  • Plus Partner Services


  • Chatter
    Feed
  • 1
    Best Answers
  • 0
    Likes Received
  • 0
    Likes Given
  • 16
    Questions
  • 25
    Replies
I am trying to create a formula to convert total number of days into years and months. But can't seem to get that to work. Has anyone got any suggestions on how to do this?
I have a web form which we are using for web to lead. When the form is filled in it automatically starts a process to send electronic contracts etc from the account.

Included on the form is a section for the customer to add the names and emails of additional contacts in the business who they would like to sign up too.

There could be a maximum of 10 extra people.

When I convert the lead, I need to convert these additional people as well. These extra people would also need to be part of the contract process. 

Is the best approach to write some custom APEX to convert these extra leads, or is there a functional way to handle this.

Also ahould I move the contract process onto the contact object or create each one fo these additional contacts as child accounts of the parent lead?

TIA
I am trying to get GoCardless Webhooks to work. They were working in sandbox but can't seem to get them to work in production. From looking at it I need to get an authentication token but I'm sure I didn't have to do this before.

This is the API Reference entry from GoCardless, can anyone give me any pointer please?

Webhooks notify you of new events in your GoCardless account (e.g., when the bank informs us that one of your payments has failed).
You can enable webhooks by creating a Webhook Endpoint in your GoCardless dashboard.
When an event occurs to a mandate or payment in your GoCardless account, a webhook will be sent to every enabled webhook endpoint as a POST request, which contains a list of events.
There are a few other things to note when using webhooks:
Webhooks may arrive out of order.
Webhooks may contain multiple events. These events need not have anything in common (i.e., they may be for different actions and resources).
When deciding what actions to take in response to Webhook events, we recommend you switch on the details[cause] field. Other fields such as details[reason_code], are payment scheme-specific and can be inconsistent between banks, whereas the details[cause] field is our simplified and predictable key indicating what triggered the event.
Webhooks with an invalid signature must return a 498 Token Invalid error.
Webhooks about unknown events should be ignored, and return 204 No Content. GoCardless may add new events to the API without considering this a backwards incompatible change.
You must use SSL/TLS for webhook URLs. Unsecured webhook URLs are only allowed in the sandbox environment.
Webhooks include an Origin header indicating what GoCardless environment they were sent from. This will be https://api.gocardless.com for
live, and https://api-sandbox.gocardless.com for sandbox.
All the webhooks you’ve ever been sent are viewable in your GoCardless dashboard in the “Developers” area.

STATUS CODES

Your webhook handler should return a response with a 2xx status code, e.g. 200 OK, or 204 No Content. If the webhook signature is invalid, you should return a 498 Invalid Token

ERRORS & RETRIES

In the
event we fail to deliver the webhook, or you respond with a non 2xx status code, we will attempt to resend the webhook up to 10 times at increasing time intervals.
You can view webhooks we’ve sent you in your GoCardless dashboard, and can retry them if required.

IP WHITELISTING

We send webhooks from the following IP addresses which you may wish to whitelist in your firewall:
37.58.102.70
37.58.102.71
We will provide advance notification by email at least two weeks before we make any changes to these addresses.
You can set the email we will contact you at from your Dashboard - simply click “Settings” in the top-right hand corner, then “Contact preferences”, and then edit your developer contact.

SIGNING WEBHOOKS

We sign the body of the POST request with an HMAC SHA256 hex digest, using the secret of the webhook endpoint for which this webhook is being sent. This is done using an additional header:
Webhook-Signature
The HMAC SHA256 hex digest of the request body.
You must check that the webhook has a valid signature before processing it. Here’s how you could do that in Ruby:
# request_signature - the signature sent in Webhook-Signature
# request_body - the JSON body of the webhook request
# secret - the secret for the webhook endpoint require "openssl" digest = OpenSSL::Digest.new("sha256") calculated_signature = OpenSSL::HMAC.hexdigest(digest, secret, request_body) if calculated_signature == request_signature
# Signature ok! else
# Invalid signature. Ignore the webhook and return 498 Token Invalid end
I had a rest webservice working in a sandbox but when I have moved it to production, I get 401 errors.
I have a custom domain (plusgroup) so the production endpoint I am using is https://plusgroup.my.salesforce.com/services/apexrest/gocardless/

I am on the EU9 instance so should I use https://eu9.salesforce.com/services/apexrest/gocardless/

are there any setting in production that would be blocking my webservice call from reaching my webservice?
I am trying to record comments in a lightning component, I have the line
 
var task_comments = component.find("comments").get("v.value");


When I click the 'update' button, the whole thing runs fine. If I click the button a second time, its fails and says 
Uncaught Action failed: c:Stopwatch$controller$onClick [component.find(...).get is not a function]

I can't understand why it works on the first click but not on the second click.

 
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 

I have 2 future calls. The first creates a record in our accounting software via call out.

I then use another future method to create an invoice in the accounting software and pass it the customer ID returned from the first future method.

I have created 2 seperate apex classes. Both are invocable processes. When a status changes on the opportunity I use process builder to fire the first call to create the customer record. Which creates a customer record related to the account.

When a customer record is created I use process builder to fire the second invocable process to create the invoice.

I am getting the error 'Future Method cannot be called from future or batch method.' 

I can't understand why if I can call the first future method from process builder, and it has finished (as a customer record is created) then why can't I call the second future method from process builder also?




 
I am struggling to write a test for the following class and struggling to understand Http Mocks, can anyone help write the test and explain how it works?
 
@RestResource(urlMapping='/gocardless/*')
global with sharing class GoCardlessEndpoints {

    
  /*  
   HttpPost method is used to capture a HttpPost request has been sent to our rest apex class.  
   Used to retrieve data coming in the request body and performing corressponding actions  
  */  
  @HttpPost  
   global static String doPost() {  
     /*  
       RestContext Class - Allows us to access the RestRequest and RestResponse objects in your Apex REST methods.   
       RestRequest class - Allows us to pass request data into our Apex RESTful Web service method.  
       RestResponse class - Allows us to pass or send back response data from our Apex RESTful web service method  
     */  
     //Returns the RestRequest object for our Apex REST method.  
     RestRequest request = RestContext.request;  
     //Returns the RestResponse for our Apex REST method.  
     RestResponse response = RestContext.response;  
     //Access the request body with input data coming in the JSON format  
     String jSONRequestBody=request.requestBody.toString().trim();  
     //Deserializes the input JSON string into an GoCardless_Event__c object  

      ResponseResult e = (ResponseResult)JSON.deserialize(jSONRequestBody, ResponseResult.class);
      
       for(integer i=0; i< e.events.size(); i++) {
     
           GoCardless_Event__c gcevent = new goCardless_Event__c();
           gcevent.name = e.events[i].Id;
           gcevent.created_at__c = e.events[i].created_at;
           gcevent.resource_type__c = e.events[i].resource_type;
           gcevent.action__c = e.events[i].action;
           if (e.events[i].resource_type == 'mandates') gcevent.Link__c = e.events[i].links.mandate;
           if (e.events[i].resource_type == 'payments') gcevent.Link__c = e.events[i].links.payment;
           
           gcevent.Customer_Number__c = getCustomerNo(gcevent.Link__c,gcevent.resource_type__c);
           gcevent.Customer_Email__c = getCustomerEmail(gcevent.Customer_Number__c);
           List <Account> Acc = [SELECT Id from Account where email__c = :gcevent.Customer_Email__c];
           for (integer a=0; a< Acc.size(); a++) {
               if (Acc.size()>0) gcevent.Account__c = Acc[a].Id;
           }
           insert gcevent;
       }
       return 'Done';
	}

    global Static String getCustomerNo(String objectId, String Endpoint) {
    
        
        system.debug(objectId);
        String Response;
        String Headers;
        

            string BaseURL= 'https://api-sandbox.gocardless.com/';
            string Token = 'sandbox_v5Szjx49RSC3H7-OrP5GKTM5emgYmaX9fgYTfCZR';
            string bearerToken = 'w-AINtjyy0k75CFBeBZbiA9cj4ebQuut-4--HsJp';            

        // Get the XML document from the external server
        Http http = new Http();
        
            HttpRequest req = new HttpRequest();
        	req.setEndpoint(BaseURL+Endpoint+'/'+objectId);
     		req.setHeader('access_token', 'Bearer '+Token);
        	req.setHeader('Authorization', 'Bearer '+Token);
        	req.setHeader('GoCardless-Version', '2015-07-06');
        	req.setHeader('Content-Type', 'application/json');
        	req.setHeader('Accept', 'application/json');
        	req.setMethod('GET');
        
        if (!Test.isRunningTest()) {
            HttpResponse res = http.send(req);
            String jSONResponseBody=res.getBody().trim();
            Integer strStart = jSONResponseBody.indexOf('customer":"')+11;
            Integer strend = jSONResponseBody.substring(strStart).indexOf('"');
            String CustNo = jSONResponseBody.substring(strStart, strStart+strend);
            return CustNo;
        }
        else return 'EV123';
      }    
    
    Static String getCustomerEmail(String objectId) {
    
        
        system.debug(objectId);
       String Response;
       String Headers;
       string BaseURL= 'https://api-sandbox.gocardless.com/customers/';
       string Token = 'sandbox_v5Szjx49RSC3H7-OrP5GKTM5emgYmaX9fgYTfCZR';
	   string bearerToken = 'w-AINtjyy0k75CFBeBZbiA9cj4ebQuut-4--HsJp';
        
        // Get the XML document from the external server
        Http http = new Http();
        HttpRequest req = new HttpRequest();
        req.setEndpoint(BaseURL+'/'+objectId);
     	req.setHeader('access_token', 'Bearer '+Token);
        req.setHeader('Authorization', 'Bearer '+Token);
        req.setHeader('GoCardless-Version', '2015-07-06');
        req.setHeader('Content-Type', 'application/json');
        req.setHeader('Accept', 'application/json');
        req.setMethod('GET');
        HttpResponse res = http.send(req);
    	String jSONResponseBody=res.getBody().trim();
        Integer strStart = jSONResponseBody.indexOf('email":"')+8;
        Integer strend = jSONResponseBody.substring(strStart).indexOf('"');
        String CustEmail = jSONResponseBody.substring(strStart, strStart+strend);
        return CustEmail;
      }    
    
    
    
    public class ResponseResult {
    
        public Event[] events;

    }
    public class Event {
        public String id, resource_type,action;
        public datetime created_at;
		public objLinks links;          
    }

    public class objLinks{
           string mandate;
           string payment;
        }




}

 
My test class only has 48% coverage, can you suggest ways to improve this?

Class
 
@RestResource(urlMapping='/gocardless/*')
global with sharing class GoCardlessEndpoints {

    
  /*  
   HttpPost method is used to capture a HttpPost request has been sent to our rest apex class.  
   Used to retrieve data coming in the request body and performing corressponding actions  
  */  
  @HttpPost  
   global static String doPost() {  
     /*  
       RestContext Class - Allows us to access the RestRequest and RestResponse objects in your Apex REST methods.   
       RestRequest class - Allows us to pass request data into our Apex RESTful Web service method.  
       RestResponse class - Allows us to pass or send back response data from our Apex RESTful web service method  
     */  
     //Returns the RestRequest object for our Apex REST method.  
     RestRequest request = RestContext.request;  
     //Returns the RestResponse for our Apex REST method.  
     RestResponse response = RestContext.response;  
     //Access the request body with input data coming in the JSON format  
     String jSONRequestBody=request.requestBody.toString().trim();  
     //Deserializes the input JSON string into an GoCardless_Event__c object  

      ResponseResult e = (ResponseResult)JSON.deserialize(jSONRequestBody, ResponseResult.class);
      
       for(integer i=0; i< e.events.size(); i++) {
     
           GoCardless_Event__c gcevent = new goCardless_Event__c();
           gcevent.name = e.events[i].Id;
           gcevent.created_at__c = e.events[i].created_at;
           gcevent.resource_type__c = e.events[i].resource_type;
           gcevent.action__c = e.events[i].action;
           if (e.events[i].resource_type == 'mandates') gcevent.Link__c = e.events[i].links.mandate;
           if (e.events[i].resource_type == 'payments') gcevent.Link__c = e.events[i].links.payment;
           
           gcevent.Customer_Number__c = getCustomerNo(gcevent.Link__c,gcevent.resource_type__c);
           gcevent.Customer_Email__c = getCustomerEmail(gcevent.Customer_Number__c);
           List <Account> Acc = [SELECT Id from Account where email__c = :gcevent.Customer_Email__c];
           for (integer a=0; a< Acc.size(); a++) {
               if (Acc.size()>0) gcevent.Account__c = Acc[a].Id;
           }
           insert gcevent;
       }
       return 'Done';
	}

    Static String getCustomerNo(String objectId, String Endpoint) {
    
        
        system.debug(objectId);
        String Response;
        String Headers;
        string BaseURL= 'https://api-sandbox.gocardless.com/';
        string Token = 'sandbox_v5Szjx49RSC3H7-OrP5GKTM5emgYmaX9fgYTfCZR';
	    string bearerToken = 'w-AINtjyy0k75CFBeBZbiA9cj4ebQuut-4--HsJp';
        
        // Get the XML document from the external server
        Http http = new Http();
        HttpRequest req = new HttpRequest();
        req.setEndpoint(BaseURL+Endpoint+'/'+objectId);
     	req.setHeader('access_token', 'Bearer '+Token);
        req.setHeader('Authorization', 'Bearer '+Token);
        req.setHeader('GoCardless-Version', '2015-07-06');
        req.setHeader('Content-Type', 'application/json');
        req.setHeader('Accept', 'application/json');
        req.setMethod('GET');
        HttpResponse res = http.send(req);
    	String jSONResponseBody=res.getBody().trim();
        Integer strStart = jSONResponseBody.indexOf('customer":"')+11;
        Integer strend = jSONResponseBody.substring(strStart).indexOf('"');
        String CustNo = jSONResponseBody.substring(strStart, strStart+strend);
        return CustNo;
      }    
    
    Static String getCustomerEmail(String objectId) {
    
        
        system.debug(objectId);
       String Response;
       String Headers;
       string BaseURL= 'https://api-sandbox.gocardless.com/customers/';
       string Token = 'sandbox_v5Szjx49RSC3H7-OrP5GKTM5emgYmaX9fgYTfCZR';
	   string bearerToken = 'w-AINtjyy0k75CFBeBZbiA9cj4ebQuut-4--HsJp';
        
        // Get the XML document from the external server
        Http http = new Http();
        HttpRequest req = new HttpRequest();
        req.setEndpoint(BaseURL+'/'+objectId);
     	req.setHeader('access_token', 'Bearer '+Token);
        req.setHeader('Authorization', 'Bearer '+Token);
        req.setHeader('GoCardless-Version', '2015-07-06');
        req.setHeader('Content-Type', 'application/json');
        req.setHeader('Accept', 'application/json');
        req.setMethod('GET');
        HttpResponse res = http.send(req);
    	String jSONResponseBody=res.getBody().trim();
        Integer strStart = jSONResponseBody.indexOf('email":"')+8;
        Integer strend = jSONResponseBody.substring(strStart).indexOf('"');
        String CustEmail = jSONResponseBody.substring(strStart, strStart+strend);
        return CustEmail;
      }    
    
    
    
    public class ResponseResult {
    
        public Event[] events;

    }
    public class Event {
        public String id, resource_type,action;
        public datetime created_at;
		public objLinks links;          
    }

    public class objLinks{
           string mandate;
           string payment;
        }




}

Test Class
 
@isTest 
public class GoCardlessEndpointsTest {

    
    static testMethod void test_doPost() {
            
            String JSONMsg = '{"events": [{"id": "EV123","email": "barry.denson@hotmail.com", "created_at": "2014-08-04T12:00:00.000Z","action": "cancelled","resource_type": "mandates","links": {"mandate": "MD123"},"details": {"origin": "bank","cause":"bank_account_disabled","description": "Your customer closed their bank account.","scheme": "bacs","reason_code": "ADDACS-B"}},{"id": "EV456","created_at": "2014-08-04T12:00:00.000Z","action": "expired","resource_type": "mandates","links": {"mandate": "MD456"},"details": {"origin": "gocardless","cause": "mandate_expired","description": "The mandate expired due to inactivity."}}]}';
    
            RestRequest req = new RestRequest();
            RestResponse res = new RestResponse();
            
            req.requestURI = '/services/apexrest/gocardless/';  //Request URL
            req.httpMethod = 'POST';							//HTTP Request Type
            req.requestBody = Blob.valueof(JSONMsg);
            
            RestContext.request = req;
            RestContext.response= res;
            
            Test.startTest();
                GoCardlessEndpoints.doPost();
            Test.StopTest();   
    }

}

 
I am getting test errors from the Apex for Xero API (https://github.com/benedwards44/Apex-for-Xero) when trying to use HTTPCalloutMock  - System.NullPointerException: Attempt to de-reference a null object 

Stack Trace:
Class.XeroCalloutUtility.executeCallout: line 33, column 1
Class.XeroAccountingApi.getContacts: line 20, column 1
Class.XeroAccountingApiTest.getContactsSuccess: line 21, column 1

This is the test class, the full code can be seen on GitHub link above. 

I have raised an issue with the developer, but I wondered if anyone could give me any pointers.
 
/**
* @author       Ben Edwards (ben@benedwards.co.nz)
* @description  Test class for the Xero API methods
**/
@isTest
public class XeroAccountingApiTest {

	/**
	* 	@author Ben Edwards (ben@benedwards.co.nz)
	*	@description Test a successful callout of the getContacts() method
	**/
	@isTest
	static void getContactsSuccess () {

		// Set the Mock Class for the callout
		Test.setMock(HttpCalloutMock.class, getStaticMock(200, 'XeroContactsMock'));

		Test.startTest();

		// Execute the callout
		List<XeroContact> xeroContacts = XeroAccountingApi.getContacts();

		Test.stopTest();

		// Assert that a contact exists
		system.assertEquals(
			1,
			xeroContacts.size(),
			'There should be one contact returned from the callout'
		);
	}


	/**
	* 	@author Ben Edwards (ben@benedwards.co.nz)
	*	@description Test an unsuccessful callout of the getContacts() method
	**/
	@isTest
	static void getContactsFail () {

		// Set the Mock Class for the callout
		Test.setMock(HttpCalloutMock.class, getStaticMock(500, 'XeroContactsMock'));

		Test.startTest();

		// Execute the callout
		try {

			List<XeroContact> xeroContacts = XeroAccountingApi.getContacts();
		} 
		catch (Exception ex) {

			// Error expected, as failed callout raises an exception
			system.assert(
				String.valueOf(ex).contains('500'),
				'The 500 error code should be contained in the string.'
			);
		}

		Test.stopTest();
	}

	/**
	* 	@author Ben Edwards (ben@benedwards.co.nz)
	*	@description Test a successful callout of the createContact() method
	**/
	@isTest
	static void createContactSuccess () {

		// Set the Mock Class for the callout
		Test.setMock(HttpCalloutMock.class, getStaticMock(200, 'XeroContactsMock'));

		Test.startTest();

		// Execute the callout
		XeroContact createdContact = XeroAccountingApi.createContact('<Contact><Name>Test Contact</Name></Contact>');

		Test.stopTest();

		// Assert that a contact exists
		system.assertNotEquals(
			null,
			createdContact,
			'The contact should be created and not null.'
		);
	}


	/**
	* 	@author Ben Edwards (ben@benedwards.co.nz)
	*	@description Test an unsuccessful callout of the createContact() method
	**/
	@isTest
	static void createContactsFail () {

		// Set the Mock Class for the callout
		Test.setMock(HttpCalloutMock.class, getStaticMock(500, 'XeroContactsMock'));

		Test.startTest();

		// Execute the callout
		try {

			XeroContact createdContact = XeroAccountingApi.createContact('<Contact><Name>Test Contact</Name></Contact>');
		} 
		catch (Exception ex) {

			// Error expected, as failed callout raises an exception
			system.assert(
				String.valueOf(ex).contains('500'),
				'The 500 error code should be contained in the string.'
			);
		}

		Test.stopTest();
	}

	/**
	* 	@author Ben Edwards (ben@benedwards.co.nz)
	*	@description Test a successful callout of the getInvoices() method
	**/
	@isTest
	static void getInvoicesSuccess() {

		// Set the Mock Class for the callout
		Test.setMock(HttpCalloutMock.class, getStaticMock(200, 'XeroInvoicesMock'));

		Test.startTest();

		// Execute the callout
		List<XeroInvoice> xeroInvoices = XeroAccountingApi.getInvoices();

		Test.stopTest();

		// Assert that a contact exists
		system.assertEquals(
			1,
			xeroInvoices.size(),
			'There should be one invoice returned from the callout'
		);
	}


	/**
	* 	@author Ben Edwards (ben@benedwards.co.nz)
	*	@description Test an unsuccessful callout of the getInvoices() method
	**/
	@isTest
	static void getInvoicesFail() {

		// Set the Mock Class for the callout
		Test.setMock(HttpCalloutMock.class, getStaticMock(500, 'XeroInvoicesMock'));

		Test.startTest();

		// Execute the callout
		try {

			List<XeroInvoice> xeroInvoices = XeroAccountingApi.getInvoices();
		} 
		catch (Exception ex) {

			// Error expected, as failed callout raises an exception
			system.assert(
				String.valueOf(ex).contains('500'),
				'The 500 error code should be contained in the string.'
			);
		}

		Test.stopTest();
	}

	/**
	* 	@author Ben Edwards (ben@benedwards.co.nz)
	*	@description Test a successful callout of the getInvoicesForContact() method
	**/
	@isTest
	static void getInvoicesForContactSuccess() {

		// Set the Mock Class for the callout
		Test.setMock(HttpCalloutMock.class, getStaticMock(200, 'XeroInvoicesMock'));

		Test.startTest();

		// Execute the callout
		List<XeroInvoice> xeroInvoices = XeroAccountingApi.getInvoicesForContact('ABC123');

		Test.stopTest();

		// Assert that a contact exists
		system.assertEquals(
			1,
			xeroInvoices.size(),
			'There should be one invoice returned from the callout'
		);
	}

	/**
	* 	@author Ben Edwards (ben@benedwards.co.nz)
	*	@description Test a successful callout of the createInvoice() method
	**/
	@isTest
	static void createInvoiceSuccess() {

		// Set the Mock Class for the callout
		Test.setMock(HttpCalloutMock.class, getStaticMock(200, 'XeroInvoicesMock'));

		Test.startTest();

		// Execute the callout
		XeroInvoice createdInvoice = XeroAccountingApi.createInvoice('<Invoice>BODY</Invoice>');

		Test.stopTest();

		// Assert that a contact exists
		system.assertNotEquals(
			null,
			createdInvoice,
			'There should be one invoice created from the callout'
		);
	}


	/**
	* 	@author Ben Edwards (ben@benedwards.co.nz)
	*	@description Test an unsuccessful callout of the createInvoice() method
	**/
	@isTest
	static void createInvoiceFail() {

		// Set the Mock Class for the callout
		Test.setMock(HttpCalloutMock.class, getStaticMock(500, 'XeroInvoicesMock'));

		Test.startTest();

		// Execute the callout
		try {

			XeroInvoice createdInvoice = XeroAccountingApi.createInvoice('<Invoice>BODY</Invoice>');
		} 
		catch (Exception ex) {

			// Error expected, as failed callout raises an exception
			system.assert(
				String.valueOf(ex).contains('500'),
				'The 500 error code should be contained in the string.'
			);
		}

		Test.stopTest();
	}

	/**
	* 	@author Ben Edwards (ben@benedwards.co.nz)
	*	@description Test scenario where no Xero Settings can be found
	**/
	@isTest
	static void getXeroSettingsError() {

		// Delete the Xero Settings created
		delete [Select Id From Xero_Settings__c];

		// Assert no Xero Settings found in Utility
		system.assertEquals(
			null,
			XeroCalloutUtility.xeroSettings.Id,
			'There should be no Xero Setting record found.'
		);

	}


	/**
	* 	@author Ben Edwards (ben@benedwards.co.nz)
	*	@description Test an unsuccessful callout of the getContacts() method
	**/
	private static StaticResourceCalloutMock getStaticMock (Integer responseCode, String mockName) {

		StaticResourceCalloutMock mock = new StaticResourceCalloutMock();
		mock.setStaticResource(mockName);
		mock.setStatusCode(responseCode);
		mock.setHeader('Content-Type', 'application/json');

		return mock;
	}


	/**
	* 	@author Ben Edwards (ben@benedwards.co.nz)
	*	@description Create test data for the method
	**/
	@testSetup
	static void setupTestData () {

		// Create a test Xero Setting record
		insert new Xero_Settings__c(
			SetupOwnerId = Userinfo.getOrganizationId(),
			Consumer_Key__c = '123456789',
			Endpoint__c = 'https://api.xero-test.com?param1=value1&param2=value2/'
		);

	}
	
}

 
I am trying to write the following JSON to a custom Object.
 
Body
{
  "events": [
    {
      "id": "EVTESTKA5SS3XP",
      "created_at": "2017-11-23T08:36:52.561Z",
      "resource_type": "mandates",
      "action": "created",
      "links": {
        "mandate": "index_ID_123"
      },
      "details": {
        "origin": "api",
        "cause": "mandate_created",
        "description": "Mandate created via the API."
      },
      "metadata": {}
    }
  ]
}

I have the following code:
 
@RestResource(urlMapping='/gocardless/*')
global with sharing class GoCardlessEndpoints {

    
  /*  
   HttpPost method is used to capture a HttpPost request has been sent to our rest apex class.  
   Used to retrieve data coming in the request body and performing corressponding actions  
  */  
  @HttpPost  
   global static String doPost() {  
     /*  
       RestContext Class - Allows us to access the RestRequest and RestResponse objects in your Apex REST methods.   
       RestRequest class - Allows us to pass request data into our Apex RESTful Web service method.  
       RestResponse class - Allows us to pass or send back response data from our Apex RESTful web service method  
     */  
     //Returns the RestRequest object for our Apex REST method.  
     RestRequest request = RestContext.request;  
     //Returns the RestResponse for our Apex REST method.  
     RestResponse response = RestContext.response;  
     //Access the request body with input data coming in the JSON format  
     String jSONRequestBody=request.requestBody.toString().trim();  
     //Deserializes the input JSON string into an GoCardless_Event__c object  

      ResponseResult e = (ResponseResult)JSON.deserialize(jSONRequestBody, ResponseResult.class);
      
       for(integer i=0; i< e.events.size(); i++) {
     
           GoCardless_Event__c gcevent = new goCardless_Event__c();
           gcevent.name = e.events[i].Id;
           gcevent.created_at__c = e.events[i].created_at;
           gcevent.resource_type__c = e.events[i].resource_type;
           gcevent.action__c = e.events[i].action;
           insert gcevent;
       }
       return 'Done';
	}

    public class ResponseResult {
    
        public Event[] events;

    }
    public class Event {
        public String id, resource_type,action;
        public datetime created_at;
		          
    }


}

I have been able to capture the event variables up to links, but how do I access the nested structures?

Thanks

Barry​
I am getting this error whilst building a proof of concept in a developer edition org. 

I understand that this is an unhandled error and in a normal web development world I would go to the logs for more info but in a SAAS situation this obviously isn't possible.

Also, as I am only using a dev org, I can't raise a ticket.

Can anyone suggest how I can progress with this?

Thanks

Barry
I'm getting errors with the component not being able to see the controller action.

error:

Uncaught Unknown controller action 'getLogEntry'
Callback failed: serviceComponent://ui.flexipage.components.page.FlexipageControllerV2/ACTION$getPage


Compnent code
<aura:component controller="CaseLogController" 
                implements="force:appHostable,flexipage:availableForAllPageTypes"
                access="global">

    <!--Include the css from static resource-->
    <ltng:require styles="{!$Resource.SLDS +
             '/assets/styles/salesforce-lightning-design-system-ltng.css'}"  afterScriptsLoaded="{!c.doScriptLoad}"/>

    <aura:attribute name="start" type="String"/>
    <aura:attribute name="stop" type="String"/>
    <aura:attribute name="sObj" type="String"/>
    <aura:attribute name="field" type="String"/>
    <aura:attribute name="stopwatch" type="Object"/>
    <aura:attribute name="LogEntry" type="Case_Log__c[]"/>
    
    <!-- Handle component initialization in a client-side controller -->
    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
    <div class="slds-clearfix slds-card">
		<div class="slds-text-heading_medium slds-align_absolute-center">
            <div aura:id="time">
                Time: 00:00:00
            </div>
        </div>
        <div class="slds-button-group slds-align_absolute-center" role="group">
                <button id="start" class="slds-button slds-button_success" onclick="{!c.onClick}">Start</button>
                <button id="stop" class="slds-button slds-button_brand" onclick="{!c.onClick}">Pause</button>
                <button id="reset" class="slds-button slds-button_destructive" onclick="{!c.onClick}">Finish</button>
        </div>
        
           <ul>
      <aura:iteration items="{!v.LogEntry}" var="log">
         <li type="dice">Entry Name : {!log.Name}</li>
         <hr/>
      </aura:iteration>
   </ul>
    </div>
</aura:component>

controller code
({
     doInit: function(component, event, helper) {
     //call apex class method
      var action = component.get("c.getLogEntry");
      action.setCallback(this, function(response) {
       //store state of response
       var state = response.getState();
       if (state === "SUCCESS") {
        //set response value in LogEntry attribute on component.
        component.set('v.LogEntry', response.getReturnValue());
       }
      });
      $A.enqueueAction(action);
     },
    doScriptLoad : function(component, event, helper) {

	},

    onClick : function(component, event, helper) {
        var div = component.find("time").getElement();
        var id = event.target.id;
        var	clsStopwatch = function() {
            // Private vars
            var	startAt	= startAt || 0;	// Time of last start / resume. (0 if not running)
            var	lapTime	= lapTime || 0;	// Time on the clock when last stopped in milliseconds

            var	now	= function() {
                return (new Date()).getTime();
            };

            // Public methods
            // Start or resume
            this.start = function() {
                
    
                startAt	= startAt ? startAt : now();
            };

            // Stop or pause
            this.stop = function() {
                // If running, update elapsed time otherwise keep it
                lapTime	= startAt ? lapTime + now() - startAt : lapTime;
                startAt	= 0; // Paused
            };

            // Reset
            this.reset = function() {
                lapTime = startAt = 0;
            };

            // Duration
            this.time = function() {
                return lapTime + (startAt ? now() - startAt : 0);
            };
        };

        var stopwatch = component.get("v.stopwatch");
        var x = stopwatch || new clsStopwatch();
        if(!stopwatch){
        	component.set("v.stopwatch", x);
        }

        function pad(num, size) {
            var s = "0000" + num;
            return s.substr(s.length - size);
        }

        function formatTime(time) {
            var h = 0;
            var m = 0;
            var s = 0;
            var newTime = '';

            h = Math.floor( time / (60 * 60 * 1000) );
            time = time % (60 * 60 * 1000);
            m = Math.floor( time / (60 * 1000) );
            time = time % (60 * 1000);
            s = Math.floor( time / 1000 );

            newTime = pad(h, 2) + ':' + pad(m, 2) + ':' + pad(s, 2); 
            return newTime;
        }

        function update() {
            div.innerHTML = "Time: " + formatTime(x.time());
        }

   		switch(id){
            case "start":
                
                setInterval(update, 1000);
                x.start();
                break;
            case "stop":
                x.stop();
                update();
                break;
            case "reset":
                x.stop();
                x.reset();
                update();
                break;
            default:
                stop();
                break;
        }
        

	}
})

Apex class code
 
public with sharing class CaseLogController {

    public static List<Case_log__c> getLogEntry() {
        list<Case_Log__c> l = [SELECT Id, Case__c, Name, In_Progress__c, Length__c, Time__c 
                             		FROM Case_Log__c WHERE In_Progress__c = true LIMIT 1];
        
        if (l.size() == 0) {
            Case_log__c newlog = new Case_log__c();
            l.add(newlog);
            insert l;
        }
        return l;
    }
    
}

 

I am using the Apex for Xero package from Ben Edwards on GitHub and I am trying to populate a Xero contact record with a Xero Address (which is a nested list) and I'm getting FATAL_ERROR System.NullPointerException: Attempt to de-reference a null object.

Looking at other questions on this subject it seems to relate to not initialising things, but I believe I am initialising everything.

Does anyone have any pointers for me? 

Thanks

Barry
global class SFtoXero {

/* XeroContact Structure
 * <Contacts>
   <Contact>
     <ContactID>bd2270c3-8706-4c11-9cfb-000b551c3f51</ContactID>
     <ContactStatus>ACTIVE</ContactStatus>
     <Name>ABC Limited</Name>
     <FirstName>Andrea</FirstName>
     <LastName>Dutchess</LastName>
     <EmailAddress>a.dutchess@abclimited.com</EmailAddress>
     <SkypeUserName>skype.dutchess@abclimited.com</SkypeUserName>
     <BankAccountDetails>45465844</BankAccountDetails>
     <TaxNumber>415465456454</TaxNumber>
     <AccountsReceivableTaxType>INPUT2</AccountsReceivableTaxType>
     <AccountsPayableTaxType>OUTPUT2</AccountsPayableTaxType>
     <Addresses>
       <Address>
         <AddressType>POBOX</AddressType>
         <AddressLine1>P O Box 123</AddressLine1>
         <City>Wellington</City>
         <PostalCode>6011</PostalCode>
         <AttentionTo>Andrea</AttentionTo>
       </Address>
       <Address>
         <AddressType>STREET</AddressType>
       </Address>
     </Addresses>
     <Phones>
       <Phone>
         <PhoneType>DEFAULT</PhoneType>
         <PhoneNumber>1111111</PhoneNumber>
         <PhoneAreaCode>04</PhoneAreaCode>
         <PhoneCountryCode>64</PhoneCountryCode>
       </Phone>
       <Phone>
         <PhoneType>FAX</PhoneType>
       </Phone>
       <Phone>
         <PhoneType>MOBILE</PhoneType>
       </Phone>
       <Phone>
         <PhoneType>DDI</PhoneType>
       </Phone>
     </Phones>
     <UpdatedDateUTC>2009-05-14T01:44:26.747</UpdatedDateUTC>
     <IsSupplier>false</IsSupplier>
     <IsCustomer>true</IsCustomer>
     <DefaultCurrency>NZD</DefaultCurrency>
   </Contact>
 </Contacts>
         
*/         
    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 City;
        @invocableVariable global string PostalCode;
        @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.city, c.AccountID);
    	}
    
    }
    
    @future(callout=true)

    public static void calloutmethod(	string practiceName, 
                                     	string partnerFirstname, 
                                     	string partnerLastname,
                                    	string emailAddress,
                                    	string City,
                                    	String AccountID){

        // Create a Contact
        // 

            XeroAddress addr = new XeroAddress();
            
            addr.AddressType ='x';
            addr.AddressLine1 = 'x';
            addr.AddressLine2 ='x';
            addr.AddressLine3 = 'x';
            addr.AddressLine4 = 'x';
            addr.City = City;
            addr.Region = 'x';                               
			addr.PostalCode = 'x';
            addr.Country = 'x';
            addr.AttentionTo = 'x';
                                            
            XeroContact newContact = new XeroContact();
                                            
            newContact.Name = practiceName;
            newContact.FirstName = partnerFirstname;
            newContact.LastName = partnerLastname;
            newContact.EmailAddress = emailAddress;
            newContact.Addresses.add(addr);                                
            
        
        // Send Contact to Xero
            try {
            	XeroContact createdContact=XeroAccountingApi.createContact(XeroXmlUtility.serialize(newContact, 'Contact'));
                system.debug('Created Contact ID: '+createdContact.ContactID);
                Xero_contact__c XeroContactinSF = new Xero_Contact__c();
                XeroContactinSF.Xero_Contact_ID__c = createdContact.ContactID;
                XeroContactinSF.Name = createdContact.Name;
                XeroContactinSF.Account__c = AccountID;
                
                insert XeroContactinSF;
			} catch (Exception ex){
            	system.debug(ex);
        	}	     
    }
    
}



 
I have an Apex method which creates a new record in Xero (accounting software).

I am trying to make the Invokable so I can use process builder to create a new Xero record when an account is created in Salesforce.

I am getting the 'You have uncommitted work pending. Please commit or rollback before calling out'

I kind of understand the reason for the error, I can't have a callout after a DML operation, but I'm unsure how to resolve it. How can I get process builder to do the callout before the DML operation when thats the trigger for the Apex to run.
I have the bare bones of a test class
 
@isTest
public class sendEmailInvocableTest {

    static testMethod void testUseCase1() {
        List<sendEmailInvocable.emailParam> Params = new List<sendEmailInvocable.emailParam>();

        
    	sendEmailInvocable.SendEmail(Params);
    }

Which works, but I am having trouble with sending data to the constructor. This is my constructor
 
public class emailParam{
		@InvocableVariable(required=true)       public String sendTo;
		@InvocableVariable        				public String ccTo;
		@InvocableVariable       				public String salutation;
		@InvocableVariable(required=true)       public String replyTo;
		@InvocableVariable(required=true)       public String senderDisplayName;
		@InvocableVariable(required=true)       public String subject;
		@InvocableVariable(required=true)       public String plainBody;
		@InvocableVariable(required=true)       public String HTMLBody;
		@InvocableVariable        				public String customVar1;
		@InvocableVariable        				public String customVar2;
		@InvocableVariable        				public String customVar3;
		@InvocableVariable        				public String customVar4;
		@InvocableVariable        				public String customVar5;

    }

How do I pass data to the constructor?

Thanks

Barry
public static void SendEmail(emailParam[] emailParams)

What is this expecting? List? 
I am trying to record comments in a lightning component, I have the line
 
var task_comments = component.find("comments").get("v.value");


When I click the 'update' button, the whole thing runs fine. If I click the button a second time, its fails and says 
Uncaught Action failed: c:Stopwatch$controller$onClick [component.find(...).get is not a function]

I can't understand why it works on the first click but not on the second click.

 
hi,

I am unable to add currency in SAR (Saudi Riyal) in lightning
Hi guys,

My requirement is to get a list of all standad and custom object apis in a list.
I dont want to list Objects like RecordType, ApexPage etc.
I want only buesness objects (Such as Account,Contact,Opportunity etc) ,and custom objects if any.
Please Help Me Out.......
online rental sytem project dummy any one can share
I have 2 future calls. The first creates a record in our accounting software via call out.

I then use another future method to create an invoice in the accounting software and pass it the customer ID returned from the first future method.

I have created 2 seperate apex classes. Both are invocable processes. When a status changes on the opportunity I use process builder to fire the first call to create the customer record. Which creates a customer record related to the account.

When a customer record is created I use process builder to fire the second invocable process to create the invoice.

I am getting the error 'Future Method cannot be called from future or batch method.' 

I can't understand why if I can call the first future method from process builder, and it has finished (as a customer record is created) then why can't I call the second future method from process builder also?




 
My test class only has 48% coverage, can you suggest ways to improve this?

Class
 
@RestResource(urlMapping='/gocardless/*')
global with sharing class GoCardlessEndpoints {

    
  /*  
   HttpPost method is used to capture a HttpPost request has been sent to our rest apex class.  
   Used to retrieve data coming in the request body and performing corressponding actions  
  */  
  @HttpPost  
   global static String doPost() {  
     /*  
       RestContext Class - Allows us to access the RestRequest and RestResponse objects in your Apex REST methods.   
       RestRequest class - Allows us to pass request data into our Apex RESTful Web service method.  
       RestResponse class - Allows us to pass or send back response data from our Apex RESTful web service method  
     */  
     //Returns the RestRequest object for our Apex REST method.  
     RestRequest request = RestContext.request;  
     //Returns the RestResponse for our Apex REST method.  
     RestResponse response = RestContext.response;  
     //Access the request body with input data coming in the JSON format  
     String jSONRequestBody=request.requestBody.toString().trim();  
     //Deserializes the input JSON string into an GoCardless_Event__c object  

      ResponseResult e = (ResponseResult)JSON.deserialize(jSONRequestBody, ResponseResult.class);
      
       for(integer i=0; i< e.events.size(); i++) {
     
           GoCardless_Event__c gcevent = new goCardless_Event__c();
           gcevent.name = e.events[i].Id;
           gcevent.created_at__c = e.events[i].created_at;
           gcevent.resource_type__c = e.events[i].resource_type;
           gcevent.action__c = e.events[i].action;
           if (e.events[i].resource_type == 'mandates') gcevent.Link__c = e.events[i].links.mandate;
           if (e.events[i].resource_type == 'payments') gcevent.Link__c = e.events[i].links.payment;
           
           gcevent.Customer_Number__c = getCustomerNo(gcevent.Link__c,gcevent.resource_type__c);
           gcevent.Customer_Email__c = getCustomerEmail(gcevent.Customer_Number__c);
           List <Account> Acc = [SELECT Id from Account where email__c = :gcevent.Customer_Email__c];
           for (integer a=0; a< Acc.size(); a++) {
               if (Acc.size()>0) gcevent.Account__c = Acc[a].Id;
           }
           insert gcevent;
       }
       return 'Done';
	}

    Static String getCustomerNo(String objectId, String Endpoint) {
    
        
        system.debug(objectId);
        String Response;
        String Headers;
        string BaseURL= 'https://api-sandbox.gocardless.com/';
        string Token = 'sandbox_v5Szjx49RSC3H7-OrP5GKTM5emgYmaX9fgYTfCZR';
	    string bearerToken = 'w-AINtjyy0k75CFBeBZbiA9cj4ebQuut-4--HsJp';
        
        // Get the XML document from the external server
        Http http = new Http();
        HttpRequest req = new HttpRequest();
        req.setEndpoint(BaseURL+Endpoint+'/'+objectId);
     	req.setHeader('access_token', 'Bearer '+Token);
        req.setHeader('Authorization', 'Bearer '+Token);
        req.setHeader('GoCardless-Version', '2015-07-06');
        req.setHeader('Content-Type', 'application/json');
        req.setHeader('Accept', 'application/json');
        req.setMethod('GET');
        HttpResponse res = http.send(req);
    	String jSONResponseBody=res.getBody().trim();
        Integer strStart = jSONResponseBody.indexOf('customer":"')+11;
        Integer strend = jSONResponseBody.substring(strStart).indexOf('"');
        String CustNo = jSONResponseBody.substring(strStart, strStart+strend);
        return CustNo;
      }    
    
    Static String getCustomerEmail(String objectId) {
    
        
        system.debug(objectId);
       String Response;
       String Headers;
       string BaseURL= 'https://api-sandbox.gocardless.com/customers/';
       string Token = 'sandbox_v5Szjx49RSC3H7-OrP5GKTM5emgYmaX9fgYTfCZR';
	   string bearerToken = 'w-AINtjyy0k75CFBeBZbiA9cj4ebQuut-4--HsJp';
        
        // Get the XML document from the external server
        Http http = new Http();
        HttpRequest req = new HttpRequest();
        req.setEndpoint(BaseURL+'/'+objectId);
     	req.setHeader('access_token', 'Bearer '+Token);
        req.setHeader('Authorization', 'Bearer '+Token);
        req.setHeader('GoCardless-Version', '2015-07-06');
        req.setHeader('Content-Type', 'application/json');
        req.setHeader('Accept', 'application/json');
        req.setMethod('GET');
        HttpResponse res = http.send(req);
    	String jSONResponseBody=res.getBody().trim();
        Integer strStart = jSONResponseBody.indexOf('email":"')+8;
        Integer strend = jSONResponseBody.substring(strStart).indexOf('"');
        String CustEmail = jSONResponseBody.substring(strStart, strStart+strend);
        return CustEmail;
      }    
    
    
    
    public class ResponseResult {
    
        public Event[] events;

    }
    public class Event {
        public String id, resource_type,action;
        public datetime created_at;
		public objLinks links;          
    }

    public class objLinks{
           string mandate;
           string payment;
        }




}

Test Class
 
@isTest 
public class GoCardlessEndpointsTest {

    
    static testMethod void test_doPost() {
            
            String JSONMsg = '{"events": [{"id": "EV123","email": "barry.denson@hotmail.com", "created_at": "2014-08-04T12:00:00.000Z","action": "cancelled","resource_type": "mandates","links": {"mandate": "MD123"},"details": {"origin": "bank","cause":"bank_account_disabled","description": "Your customer closed their bank account.","scheme": "bacs","reason_code": "ADDACS-B"}},{"id": "EV456","created_at": "2014-08-04T12:00:00.000Z","action": "expired","resource_type": "mandates","links": {"mandate": "MD456"},"details": {"origin": "gocardless","cause": "mandate_expired","description": "The mandate expired due to inactivity."}}]}';
    
            RestRequest req = new RestRequest();
            RestResponse res = new RestResponse();
            
            req.requestURI = '/services/apexrest/gocardless/';  //Request URL
            req.httpMethod = 'POST';							//HTTP Request Type
            req.requestBody = Blob.valueof(JSONMsg);
            
            RestContext.request = req;
            RestContext.response= res;
            
            Test.startTest();
                GoCardlessEndpoints.doPost();
            Test.StopTest();   
    }

}

 
I am getting test errors from the Apex for Xero API (https://github.com/benedwards44/Apex-for-Xero) when trying to use HTTPCalloutMock  - System.NullPointerException: Attempt to de-reference a null object 

Stack Trace:
Class.XeroCalloutUtility.executeCallout: line 33, column 1
Class.XeroAccountingApi.getContacts: line 20, column 1
Class.XeroAccountingApiTest.getContactsSuccess: line 21, column 1

This is the test class, the full code can be seen on GitHub link above. 

I have raised an issue with the developer, but I wondered if anyone could give me any pointers.
 
/**
* @author       Ben Edwards (ben@benedwards.co.nz)
* @description  Test class for the Xero API methods
**/
@isTest
public class XeroAccountingApiTest {

	/**
	* 	@author Ben Edwards (ben@benedwards.co.nz)
	*	@description Test a successful callout of the getContacts() method
	**/
	@isTest
	static void getContactsSuccess () {

		// Set the Mock Class for the callout
		Test.setMock(HttpCalloutMock.class, getStaticMock(200, 'XeroContactsMock'));

		Test.startTest();

		// Execute the callout
		List<XeroContact> xeroContacts = XeroAccountingApi.getContacts();

		Test.stopTest();

		// Assert that a contact exists
		system.assertEquals(
			1,
			xeroContacts.size(),
			'There should be one contact returned from the callout'
		);
	}


	/**
	* 	@author Ben Edwards (ben@benedwards.co.nz)
	*	@description Test an unsuccessful callout of the getContacts() method
	**/
	@isTest
	static void getContactsFail () {

		// Set the Mock Class for the callout
		Test.setMock(HttpCalloutMock.class, getStaticMock(500, 'XeroContactsMock'));

		Test.startTest();

		// Execute the callout
		try {

			List<XeroContact> xeroContacts = XeroAccountingApi.getContacts();
		} 
		catch (Exception ex) {

			// Error expected, as failed callout raises an exception
			system.assert(
				String.valueOf(ex).contains('500'),
				'The 500 error code should be contained in the string.'
			);
		}

		Test.stopTest();
	}

	/**
	* 	@author Ben Edwards (ben@benedwards.co.nz)
	*	@description Test a successful callout of the createContact() method
	**/
	@isTest
	static void createContactSuccess () {

		// Set the Mock Class for the callout
		Test.setMock(HttpCalloutMock.class, getStaticMock(200, 'XeroContactsMock'));

		Test.startTest();

		// Execute the callout
		XeroContact createdContact = XeroAccountingApi.createContact('<Contact><Name>Test Contact</Name></Contact>');

		Test.stopTest();

		// Assert that a contact exists
		system.assertNotEquals(
			null,
			createdContact,
			'The contact should be created and not null.'
		);
	}


	/**
	* 	@author Ben Edwards (ben@benedwards.co.nz)
	*	@description Test an unsuccessful callout of the createContact() method
	**/
	@isTest
	static void createContactsFail () {

		// Set the Mock Class for the callout
		Test.setMock(HttpCalloutMock.class, getStaticMock(500, 'XeroContactsMock'));

		Test.startTest();

		// Execute the callout
		try {

			XeroContact createdContact = XeroAccountingApi.createContact('<Contact><Name>Test Contact</Name></Contact>');
		} 
		catch (Exception ex) {

			// Error expected, as failed callout raises an exception
			system.assert(
				String.valueOf(ex).contains('500'),
				'The 500 error code should be contained in the string.'
			);
		}

		Test.stopTest();
	}

	/**
	* 	@author Ben Edwards (ben@benedwards.co.nz)
	*	@description Test a successful callout of the getInvoices() method
	**/
	@isTest
	static void getInvoicesSuccess() {

		// Set the Mock Class for the callout
		Test.setMock(HttpCalloutMock.class, getStaticMock(200, 'XeroInvoicesMock'));

		Test.startTest();

		// Execute the callout
		List<XeroInvoice> xeroInvoices = XeroAccountingApi.getInvoices();

		Test.stopTest();

		// Assert that a contact exists
		system.assertEquals(
			1,
			xeroInvoices.size(),
			'There should be one invoice returned from the callout'
		);
	}


	/**
	* 	@author Ben Edwards (ben@benedwards.co.nz)
	*	@description Test an unsuccessful callout of the getInvoices() method
	**/
	@isTest
	static void getInvoicesFail() {

		// Set the Mock Class for the callout
		Test.setMock(HttpCalloutMock.class, getStaticMock(500, 'XeroInvoicesMock'));

		Test.startTest();

		// Execute the callout
		try {

			List<XeroInvoice> xeroInvoices = XeroAccountingApi.getInvoices();
		} 
		catch (Exception ex) {

			// Error expected, as failed callout raises an exception
			system.assert(
				String.valueOf(ex).contains('500'),
				'The 500 error code should be contained in the string.'
			);
		}

		Test.stopTest();
	}

	/**
	* 	@author Ben Edwards (ben@benedwards.co.nz)
	*	@description Test a successful callout of the getInvoicesForContact() method
	**/
	@isTest
	static void getInvoicesForContactSuccess() {

		// Set the Mock Class for the callout
		Test.setMock(HttpCalloutMock.class, getStaticMock(200, 'XeroInvoicesMock'));

		Test.startTest();

		// Execute the callout
		List<XeroInvoice> xeroInvoices = XeroAccountingApi.getInvoicesForContact('ABC123');

		Test.stopTest();

		// Assert that a contact exists
		system.assertEquals(
			1,
			xeroInvoices.size(),
			'There should be one invoice returned from the callout'
		);
	}

	/**
	* 	@author Ben Edwards (ben@benedwards.co.nz)
	*	@description Test a successful callout of the createInvoice() method
	**/
	@isTest
	static void createInvoiceSuccess() {

		// Set the Mock Class for the callout
		Test.setMock(HttpCalloutMock.class, getStaticMock(200, 'XeroInvoicesMock'));

		Test.startTest();

		// Execute the callout
		XeroInvoice createdInvoice = XeroAccountingApi.createInvoice('<Invoice>BODY</Invoice>');

		Test.stopTest();

		// Assert that a contact exists
		system.assertNotEquals(
			null,
			createdInvoice,
			'There should be one invoice created from the callout'
		);
	}


	/**
	* 	@author Ben Edwards (ben@benedwards.co.nz)
	*	@description Test an unsuccessful callout of the createInvoice() method
	**/
	@isTest
	static void createInvoiceFail() {

		// Set the Mock Class for the callout
		Test.setMock(HttpCalloutMock.class, getStaticMock(500, 'XeroInvoicesMock'));

		Test.startTest();

		// Execute the callout
		try {

			XeroInvoice createdInvoice = XeroAccountingApi.createInvoice('<Invoice>BODY</Invoice>');
		} 
		catch (Exception ex) {

			// Error expected, as failed callout raises an exception
			system.assert(
				String.valueOf(ex).contains('500'),
				'The 500 error code should be contained in the string.'
			);
		}

		Test.stopTest();
	}

	/**
	* 	@author Ben Edwards (ben@benedwards.co.nz)
	*	@description Test scenario where no Xero Settings can be found
	**/
	@isTest
	static void getXeroSettingsError() {

		// Delete the Xero Settings created
		delete [Select Id From Xero_Settings__c];

		// Assert no Xero Settings found in Utility
		system.assertEquals(
			null,
			XeroCalloutUtility.xeroSettings.Id,
			'There should be no Xero Setting record found.'
		);

	}


	/**
	* 	@author Ben Edwards (ben@benedwards.co.nz)
	*	@description Test an unsuccessful callout of the getContacts() method
	**/
	private static StaticResourceCalloutMock getStaticMock (Integer responseCode, String mockName) {

		StaticResourceCalloutMock mock = new StaticResourceCalloutMock();
		mock.setStaticResource(mockName);
		mock.setStatusCode(responseCode);
		mock.setHeader('Content-Type', 'application/json');

		return mock;
	}


	/**
	* 	@author Ben Edwards (ben@benedwards.co.nz)
	*	@description Create test data for the method
	**/
	@testSetup
	static void setupTestData () {

		// Create a test Xero Setting record
		insert new Xero_Settings__c(
			SetupOwnerId = Userinfo.getOrganizationId(),
			Consumer_Key__c = '123456789',
			Endpoint__c = 'https://api.xero-test.com?param1=value1&param2=value2/'
		);

	}
	
}

 
I am trying to write the following JSON to a custom Object.
 
Body
{
  "events": [
    {
      "id": "EVTESTKA5SS3XP",
      "created_at": "2017-11-23T08:36:52.561Z",
      "resource_type": "mandates",
      "action": "created",
      "links": {
        "mandate": "index_ID_123"
      },
      "details": {
        "origin": "api",
        "cause": "mandate_created",
        "description": "Mandate created via the API."
      },
      "metadata": {}
    }
  ]
}

I have the following code:
 
@RestResource(urlMapping='/gocardless/*')
global with sharing class GoCardlessEndpoints {

    
  /*  
   HttpPost method is used to capture a HttpPost request has been sent to our rest apex class.  
   Used to retrieve data coming in the request body and performing corressponding actions  
  */  
  @HttpPost  
   global static String doPost() {  
     /*  
       RestContext Class - Allows us to access the RestRequest and RestResponse objects in your Apex REST methods.   
       RestRequest class - Allows us to pass request data into our Apex RESTful Web service method.  
       RestResponse class - Allows us to pass or send back response data from our Apex RESTful web service method  
     */  
     //Returns the RestRequest object for our Apex REST method.  
     RestRequest request = RestContext.request;  
     //Returns the RestResponse for our Apex REST method.  
     RestResponse response = RestContext.response;  
     //Access the request body with input data coming in the JSON format  
     String jSONRequestBody=request.requestBody.toString().trim();  
     //Deserializes the input JSON string into an GoCardless_Event__c object  

      ResponseResult e = (ResponseResult)JSON.deserialize(jSONRequestBody, ResponseResult.class);
      
       for(integer i=0; i< e.events.size(); i++) {
     
           GoCardless_Event__c gcevent = new goCardless_Event__c();
           gcevent.name = e.events[i].Id;
           gcevent.created_at__c = e.events[i].created_at;
           gcevent.resource_type__c = e.events[i].resource_type;
           gcevent.action__c = e.events[i].action;
           insert gcevent;
       }
       return 'Done';
	}

    public class ResponseResult {
    
        public Event[] events;

    }
    public class Event {
        public String id, resource_type,action;
        public datetime created_at;
		          
    }


}

I have been able to capture the event variables up to links, but how do I access the nested structures?

Thanks

Barry​
Hi,

While going thorugh the trailhead for lightning, I came across "slds-grow".

Could somebody please explain me the use and effect of slds-grow.

Thanks in Advance !
I am getting this error whilst building a proof of concept in a developer edition org. 

I understand that this is an unhandled error and in a normal web development world I would go to the logs for more info but in a SAAS situation this obviously isn't possible.

Also, as I am only using a dev org, I can't raise a ticket.

Can anyone suggest how I can progress with this?

Thanks

Barry
I have been using the Volunteer Job Listing tool that was made for my organization in our Salesforce build with different campaigns for months but all of a sudden the sign up button has stopped working. Volunteers are no longer able to sign up for a job because the button is no longer click-able even though it used to open up a pop-up inviting them to fill out their personal information. We depend a lot on volunteer sign-up so this issue is critical for my organization. Here is where the object is hosted in an iframe our website: http://kidscodejeunesse.org/volunteer.html
I'm getting errors with the component not being able to see the controller action.

error:

Uncaught Unknown controller action 'getLogEntry'
Callback failed: serviceComponent://ui.flexipage.components.page.FlexipageControllerV2/ACTION$getPage


Compnent code
<aura:component controller="CaseLogController" 
                implements="force:appHostable,flexipage:availableForAllPageTypes"
                access="global">

    <!--Include the css from static resource-->
    <ltng:require styles="{!$Resource.SLDS +
             '/assets/styles/salesforce-lightning-design-system-ltng.css'}"  afterScriptsLoaded="{!c.doScriptLoad}"/>

    <aura:attribute name="start" type="String"/>
    <aura:attribute name="stop" type="String"/>
    <aura:attribute name="sObj" type="String"/>
    <aura:attribute name="field" type="String"/>
    <aura:attribute name="stopwatch" type="Object"/>
    <aura:attribute name="LogEntry" type="Case_Log__c[]"/>
    
    <!-- Handle component initialization in a client-side controller -->
    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
    <div class="slds-clearfix slds-card">
		<div class="slds-text-heading_medium slds-align_absolute-center">
            <div aura:id="time">
                Time: 00:00:00
            </div>
        </div>
        <div class="slds-button-group slds-align_absolute-center" role="group">
                <button id="start" class="slds-button slds-button_success" onclick="{!c.onClick}">Start</button>
                <button id="stop" class="slds-button slds-button_brand" onclick="{!c.onClick}">Pause</button>
                <button id="reset" class="slds-button slds-button_destructive" onclick="{!c.onClick}">Finish</button>
        </div>
        
           <ul>
      <aura:iteration items="{!v.LogEntry}" var="log">
         <li type="dice">Entry Name : {!log.Name}</li>
         <hr/>
      </aura:iteration>
   </ul>
    </div>
</aura:component>

controller code
({
     doInit: function(component, event, helper) {
     //call apex class method
      var action = component.get("c.getLogEntry");
      action.setCallback(this, function(response) {
       //store state of response
       var state = response.getState();
       if (state === "SUCCESS") {
        //set response value in LogEntry attribute on component.
        component.set('v.LogEntry', response.getReturnValue());
       }
      });
      $A.enqueueAction(action);
     },
    doScriptLoad : function(component, event, helper) {

	},

    onClick : function(component, event, helper) {
        var div = component.find("time").getElement();
        var id = event.target.id;
        var	clsStopwatch = function() {
            // Private vars
            var	startAt	= startAt || 0;	// Time of last start / resume. (0 if not running)
            var	lapTime	= lapTime || 0;	// Time on the clock when last stopped in milliseconds

            var	now	= function() {
                return (new Date()).getTime();
            };

            // Public methods
            // Start or resume
            this.start = function() {
                
    
                startAt	= startAt ? startAt : now();
            };

            // Stop or pause
            this.stop = function() {
                // If running, update elapsed time otherwise keep it
                lapTime	= startAt ? lapTime + now() - startAt : lapTime;
                startAt	= 0; // Paused
            };

            // Reset
            this.reset = function() {
                lapTime = startAt = 0;
            };

            // Duration
            this.time = function() {
                return lapTime + (startAt ? now() - startAt : 0);
            };
        };

        var stopwatch = component.get("v.stopwatch");
        var x = stopwatch || new clsStopwatch();
        if(!stopwatch){
        	component.set("v.stopwatch", x);
        }

        function pad(num, size) {
            var s = "0000" + num;
            return s.substr(s.length - size);
        }

        function formatTime(time) {
            var h = 0;
            var m = 0;
            var s = 0;
            var newTime = '';

            h = Math.floor( time / (60 * 60 * 1000) );
            time = time % (60 * 60 * 1000);
            m = Math.floor( time / (60 * 1000) );
            time = time % (60 * 1000);
            s = Math.floor( time / 1000 );

            newTime = pad(h, 2) + ':' + pad(m, 2) + ':' + pad(s, 2); 
            return newTime;
        }

        function update() {
            div.innerHTML = "Time: " + formatTime(x.time());
        }

   		switch(id){
            case "start":
                
                setInterval(update, 1000);
                x.start();
                break;
            case "stop":
                x.stop();
                update();
                break;
            case "reset":
                x.stop();
                x.reset();
                update();
                break;
            default:
                stop();
                break;
        }
        

	}
})

Apex class code
 
public with sharing class CaseLogController {

    public static List<Case_log__c> getLogEntry() {
        list<Case_Log__c> l = [SELECT Id, Case__c, Name, In_Progress__c, Length__c, Time__c 
                             		FROM Case_Log__c WHERE In_Progress__c = true LIMIT 1];
        
        if (l.size() == 0) {
            Case_log__c newlog = new Case_log__c();
            l.add(newlog);
            insert l;
        }
        return l;
    }
    
}

 
can anyone please suggest how to create a dependent hirerachy checkbox using salesforce lightning. something like
- ab
  - abc
  -abcd

so if ab is checked so both abc & abcd should be checked and if any of the sub chek box is checked then ab is check....please suggest​

I am using the Apex for Xero package from Ben Edwards on GitHub and I am trying to populate a Xero contact record with a Xero Address (which is a nested list) and I'm getting FATAL_ERROR System.NullPointerException: Attempt to de-reference a null object.

Looking at other questions on this subject it seems to relate to not initialising things, but I believe I am initialising everything.

Does anyone have any pointers for me? 

Thanks

Barry
global class SFtoXero {

/* XeroContact Structure
 * <Contacts>
   <Contact>
     <ContactID>bd2270c3-8706-4c11-9cfb-000b551c3f51</ContactID>
     <ContactStatus>ACTIVE</ContactStatus>
     <Name>ABC Limited</Name>
     <FirstName>Andrea</FirstName>
     <LastName>Dutchess</LastName>
     <EmailAddress>a.dutchess@abclimited.com</EmailAddress>
     <SkypeUserName>skype.dutchess@abclimited.com</SkypeUserName>
     <BankAccountDetails>45465844</BankAccountDetails>
     <TaxNumber>415465456454</TaxNumber>
     <AccountsReceivableTaxType>INPUT2</AccountsReceivableTaxType>
     <AccountsPayableTaxType>OUTPUT2</AccountsPayableTaxType>
     <Addresses>
       <Address>
         <AddressType>POBOX</AddressType>
         <AddressLine1>P O Box 123</AddressLine1>
         <City>Wellington</City>
         <PostalCode>6011</PostalCode>
         <AttentionTo>Andrea</AttentionTo>
       </Address>
       <Address>
         <AddressType>STREET</AddressType>
       </Address>
     </Addresses>
     <Phones>
       <Phone>
         <PhoneType>DEFAULT</PhoneType>
         <PhoneNumber>1111111</PhoneNumber>
         <PhoneAreaCode>04</PhoneAreaCode>
         <PhoneCountryCode>64</PhoneCountryCode>
       </Phone>
       <Phone>
         <PhoneType>FAX</PhoneType>
       </Phone>
       <Phone>
         <PhoneType>MOBILE</PhoneType>
       </Phone>
       <Phone>
         <PhoneType>DDI</PhoneType>
       </Phone>
     </Phones>
     <UpdatedDateUTC>2009-05-14T01:44:26.747</UpdatedDateUTC>
     <IsSupplier>false</IsSupplier>
     <IsCustomer>true</IsCustomer>
     <DefaultCurrency>NZD</DefaultCurrency>
   </Contact>
 </Contacts>
         
*/         
    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 City;
        @invocableVariable global string PostalCode;
        @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.city, c.AccountID);
    	}
    
    }
    
    @future(callout=true)

    public static void calloutmethod(	string practiceName, 
                                     	string partnerFirstname, 
                                     	string partnerLastname,
                                    	string emailAddress,
                                    	string City,
                                    	String AccountID){

        // Create a Contact
        // 

            XeroAddress addr = new XeroAddress();
            
            addr.AddressType ='x';
            addr.AddressLine1 = 'x';
            addr.AddressLine2 ='x';
            addr.AddressLine3 = 'x';
            addr.AddressLine4 = 'x';
            addr.City = City;
            addr.Region = 'x';                               
			addr.PostalCode = 'x';
            addr.Country = 'x';
            addr.AttentionTo = 'x';
                                            
            XeroContact newContact = new XeroContact();
                                            
            newContact.Name = practiceName;
            newContact.FirstName = partnerFirstname;
            newContact.LastName = partnerLastname;
            newContact.EmailAddress = emailAddress;
            newContact.Addresses.add(addr);                                
            
        
        // Send Contact to Xero
            try {
            	XeroContact createdContact=XeroAccountingApi.createContact(XeroXmlUtility.serialize(newContact, 'Contact'));
                system.debug('Created Contact ID: '+createdContact.ContactID);
                Xero_contact__c XeroContactinSF = new Xero_Contact__c();
                XeroContactinSF.Xero_Contact_ID__c = createdContact.ContactID;
                XeroContactinSF.Name = createdContact.Name;
                XeroContactinSF.Account__c = AccountID;
                
                insert XeroContactinSF;
			} catch (Exception ex){
            	system.debug(ex);
        	}	     
    }
    
}