Showing posts with label Salesforce. Show all posts
Showing posts with label Salesforce. Show all posts

Thursday, 12 July 2007

SMS Alerts from Salesforce - A Confession

I did promise to create a CodeProject article covering this development project.

Unfortunately I had to rebuild my laptop and in the process lost my code. School-boy error. Good news is that I've got the posts to get the code from and a flight to Australia coming up. I'll get it up soon.

Thursday, 17 May 2007

SMS Alerts from Salesforce [3] - Send an SMS

I've realised two things writing this series of posts.

  1. I need a different template if I'm going to include code samples
  2. My blog host does not support uploading source code and projects for download

So, once I've complete this project, I'll collate it all and summarise it as a Code Project article. I'll post the link when I'm done.

So back to the matter at hand. We've completed the setup of the various elements, time to start tying them together. Today's post focuses on developing the code in the notifications method of the CaseAlert web service.

The first step is to retrieve the Case object passed through to the notification

CaseNotification caseNotification = (CaseNotification)notifications1.Notification[0];
Case caseItem = (Case)caseNotification.sObject;

I then setup the binding to the Salesforce API using a proxy I made earlier from the Salesforce WSDL. I passed in the Session ID contained with the notification. One gotcha, you have to specify the Url value which should be passed in by the calling service. This meant I had to add it in to the test harness application.

sforce.SforceService salesforceBinding = new sforce.SforceService();
salesforceBinding.SessionHeaderValue = new sforce.SessionHeader();
salesforceBinding.SessionHeaderValue.sessionId = notifications1.SessionId;
salesforceBinding.Url = notifications1.EnterpriseUrl;

Now I'm getting a lightweight Account object containing just the Name and OwnerID for the account. If you're new to the Salesforce API this retrieval of objects by specifying the fields and object type along with the ID can seem a bit arcane. It's actually really useful as by keeping the interface generic it allows you to request custom fields as well as those that are inbuilt.

sforce.sObject[] ret = salesforceBinding.retrieve("OwnerId,Name", "Account", new string[] { caseItem.AccountId });
sforce.Account caseAccount = (sforce.Account)ret[0];

Doing the same this time with the User object which corresponds to the Owner of the account.

ret = salesforceBinding.retrieve("MobilePhone", "User", new string[] { caseAccount.OwnerId });
sforce.User accountOwner = (sforce.User)ret[0];

The SMS message body is constructed using the data retrieved in the previous steps. If you want to include different parameters then just get more fields the Account and Owner objects.

string messageBody = string.Format("Your account {0} has opened a new Case ref:{1}. Call support on 0845 356 5759 for details", caseAccount.Name, caseItem.CaseNumber);

Now the really easy bit ;) sending an SMS. For this code snippet I've create a web reference called esendex to the SendService of the Esendex XML Web Service. You will need to have an account with us and enter the appropriate credentials to use this code as is. If you haven't got an account yet, then feel free to setup a trial.

esendex.MessengerHeader header = new esendex.MessengerHeader();
header.Account = "EX0000000";
header.Username = "user@company.com";
header.Password = "xxxxxxx";
esendex.SendService sendService = new esendex.SendService();
sendService.MessengerHeaderValue = header;
sendService.SendMessage(accountOwner.MobilePhone, messageBody, esendex.MessageType.Text);

Return a ACK response back to the calling service.

return new notificationsResponse();

Would you believe it, it worked first time! Well, after I'd set the EnterpriseURL property in the test client.

Next time I'm going to setup the workflow rule in Salesforce and see if the whole thing hangs together.

Sunday, 13 May 2007

SMS Alerts from Salesforce [2] - Creating the Web Service

Once you've defined the outbound message and specified and Endpoint URL, when you view the definition you'll see a link to the WSDL. This is the interface definition for the web service that the service generation tool will require to create the web service code.

The .net framework SDK ships with a file called wsdl.exe. This is most often used for generating web service client proxies but used with the /server switch, it generates an abstract server class that forms the basis of our web service. In the default installation of Visual Studio 2005, it can be found in C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin.

So I downloaded the WSDL to a file call CaseAlert.wsdl and ran the following command line

wsdl.exe /server CaseAlert.wsdl

This created a file called NotificationService.cs containing the abstract service class and supporting data types to enable the service to receive calls from the Salesforce systems.

I then created a web project called salesforcelistener and dragged the NotificationService.cs file into the App_Code folder. My initial thought was to create a new WebService called CaseAlert in this project and inherit from the abstract NotificationService class.

Unfortunately this didn't work, the methods of the base class were not exposed as a web service methods. I'm guessing this is because of the reflection algorithms that are used to generate the WSDL. So I deleted the CaseAlert code behind page and turned the NotificationService into a concrete class for the CaseAlert web service.

[System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "2.0.50727.42")]
[System.Web.Services.WebServiceAttribute(Namespace = "http://soap.sforce.com/2005/09/outbound")]
[System.Web.Services.WebServiceBindingAttribute(Name = "NotificationBinding", Namespace = "http://soap.sforce.com/2005/09/outbound")]
[System.Xml.Serialization.XmlIncludeAttribute(typeof(sObject))]
public class CaseAlert : System.Web.Services.WebService
{
[System.Web.Services.WebMethodAttribute()]
[System.Web.Services.Protocols.SoapDocumentMethodAttribute("", Use = System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle = System.Web.Services.Protocols.SoapParameterStyle.Bare)]
[return: System.Xml.Serialization.XmlElementAttribute("notificationsResponse", Namespace = "http://soap.sforce.com/2005/09/outbound")]
public notificationsResponse notifications([System.Xml.Serialization.XmlElementAttribute("notifications", Namespace = "http://soap.sforce.com/2005/09/outbound")] notifications notifications1)
{
return new notificationsResponse();
}
}

Fired up the project and success:

Test Harness

Rather than use Salesforce workflows to generate test messages, and potentially annoy the live users of the system, I generated a harness so test messages could be fired at will. As this is essentially a web service this is incredibly simple.

While the web service project was running, I created a new console application project. Created a web reference from this to my web service (called salesforceoutbind) so I can make calls as required.

Remember the Session ID, this is value returned in response to a successful login request to the Salesforce API and can then be used for all subsequent calls to the API. So I added in the proxy cs file supplied with in the Salesforce sample C#.net application which includes all the code required to set this up.

The code for my console app is below. Logging and error handling excluded for brevity.

static void Main(string[] args)
{
SforceService salesforceBinding = new SforceService();
// amend these with your details
LoginResult loginResult = salesforceBinding.login("user.name@company.com", "password");

salesforceoutbind.notifications n = new salesforceoutbind.notifications();
salesforceoutbind.CaseNotification cn = new salesforceoutbind.CaseNotification();
salesforceoutbind.Case c = new salesforceoutbind.Case();
cn.sObject = c;
c.AccountId = "00120000004SPkT";
c.CaseNumber = "001000";
c.Subject = "My New Case";

n.Notification = new salesforcelistenerclient.salesforceoutbind.CaseNotification[] { cn };

n.SessionId = loginResult.sessionId;

salesforceoutbind.CaseAlert alert = new salesforcelistenerclient.salesforceoutbind.CaseAlert();
alert.notifications(n);
}

I can now push messages to the listener with complete control over the data that is passed in. Next time I'll call back into the Salesforce API to get the contact details for the Account Manager and send them a message using the Esendex SMS XML Web Service API

Saturday, 12 May 2007

SMS Alerts from Salesforce [1] - Generating Alerts

This sample is going to generate an SMS alert to a customer's account manager whenever they log a Case. Alerts from Salesforce are generated as calls to external web services in response to workflow events. In Salesforce parlance they are called OutboundMessages.

Defining your Outbound Message

The first task is to setup an Outbound Mesage. When logged into Salesforce go to:

Setup > App Setup > Customize > Workflows & Approvals > Outbound Messages

The way outbound messages work, are to post an object to the external service. First step is to choose the object to post. In this example, we're going to choose Case.

Clicking next brings you to the definition screen where you specify:

  • message name
  • Endpoint URL (his doesn't need to exist while you're setting this up)
  • whether to pass the Session ID (checked, see below)
  • the fields from the Case object you want pushed to the endpoint.

The Session ID is important in our case becase we're going to need to call back into the Salesforce API to retrieve details of the Account (we've changed this to Customer in our installation so forgive me if I get this wrong going forward) against which the Case was raised and then onto the contact details for the Account Manager. Passing the Session ID, saves us from having to store username and password details elsewhere. I think it's a really neat feature of their API

Clicking Save gives you the completed message definition, mine's below.

We're now in a position to create the web service to receive the outbound messages, which will be the subject of the next post.

Wednesday, 9 May 2007

SMS Alerts from Salesforce [intro]

We're currently evaluating Salesforce.com for use as a Customer Support system. All of our sales team use for it for managing their opportunities and it seems a natural progression. If nothing else it's one less system to integrate with and keep the customer records synched with.

One of the key requirements of any support system is SLA (Service Level Agreement) monitoring and alerting. We need to be sure we are meeting the SLAs we have in place with our customers.

The Salesforce.com pre-sales support team are assisting us with setting up the workflows. I thought it would be pretty useful to have SMS messages sent out when key stages were reached, for example:

  • SLAs were in danger of being breached for a particular case
  • A key account logs a call and the account manager want's to made aware immediately
  • The number of outstanding calls reaches a threshold value and the manager needs informing

I will be developing a simple system that receives notification from the Salesforce.com API, retrieves any additional data required and sends an SMS using the Esendex SMS API.

Full source code will be provided (C#.Net) so check back on progress and see how I get on.

Tuesday, 20 March 2007

Fun with the Salesforce API and custom fields

One of our marketing approaches is to offer a sms service trial of our Business SMS Service. This enables prospects to fully test the two-way SMS service before committing to one of our reasonably priced ;-) monthly plans.

We have recently adopted salesforce.com to improve our CRM and Sales processes and wanted to push all new trials into salesforce so our sales teams could follow them up.

We put together a simple application that utilises the salesforce web servce API to create Lead objects in salesforce and assign them to appropriate queues based on the prospects country. This was very simple and we were very quickly up and running but hit a stumbling block when we came to custom fields.

We've customised the Lead definition in our salesforce implementation to include URL fields that can link directly into our account management system. Making it very easy for the sales teams to manage the service features and promote the SMS service account when a customer decides to sign up.

The code samples for C# were a little sketchy when it came to custom fields. We spent a long time looking at blogs and forums trying to workout how to get these values pushed in. Then the lightbulb finally lit up.

Salesforce seems to have a generic datastore with loose descriptions of objects that contain collections of fields. The web service proxies that were supplied as part of the sample kit also all inheritied from sObject as below:

[System.Xml.Serialization.XmlTypeAttribute(Namespace="urn:sobject.enterprise.soap.sforce.com")] public class Lead : sObject {

Upon further inspection, we discovered that XML serialisation attributes had been set on all fields in the Lead class, eg:

[System.Xml.Serialization.XmlElementAttribute(IsNullable=true)] public string Company;

Eureka! We just had to add additional fields for our custom fields and it worked, eg:

[System.Xml.Serialization.XmlElementAttribute(IsNullable = true)] public string AMS_Account_URL__c;

Note the field name is the API version of the field name. From within salesforce go to Setup > App Setup > Customize > Leads (or whichever object definition you want) > Fields and then click on the appropriate field to find this out.

So now whenever someone registers for a free trial of our Business SMS Service, salesforce is updated and we can assist them straight away.