Service and Support Blog

Service and Support Blog

Service and Support Blog - March 2009

  • Bring Twitter into Your Service Cloud with Salesforce CRM for Twitter

    Erica Kuhl Mar 23, 2009

    Today salesforce.com introduced Salesforce CRM for Twitter, bringing together Salesforce CRM with Twitter.

    Twitter provides a free platform for users to answer the question “What are you doing?” in 140 characters or less and broadcast the answer to their friends and the world at large. While most of these “tweets” talk about the every day details of people’s lives, with more than 8 million users, you can bet that any number of topics are being discussed at any given time, including specific brands and products. Because of this, many companies want to be a part of the Twitter community and connect directly with customers in the more casual world of Twitter. Salesforce CRM for Twitter and the Service Cloud give companies an easy way to join conversations happening on Twitter by enabling:

    • Search: Salesforce CRM for Twitter helps companies search through the millions of "tweets" happening on Twitter every day to find the relevant conversations - all from within the Service Cloud.
    • Monitor: After identifying an appropriate "tweet," a company can capture and monitor the conversation by creating a record in the Service Cloud that tracks the original post and all subsequent replies.
    • Join: Salesforce CRM for Twitter empowers enterprises to be active participants on Twitter by enabling them to funnel relevant solutions from the Service Cloud knowledge base into a Twitter post, effectively joining the conversation.

    Salesforce CRM for Twitter is currently scheduled to be available at no additional charge in the summer of 2009.

    For more information check out http://www.salesforce.com/servicecloud or follow salesforce.com Twitter @ successforce.

    Service Cloud

  • Reporting On Percentage Of Cases By Origin

    Marco Casalaina Mar 19, 2009

    Today's blog post comes courtesy of guest blogger Jay Thayer, our resident all-around Salesforce.com guru.

    Let's say you're trying to build a common report, Cases By Origin, but with a twist -- you want to know what percent of your interactions are coming via each channel.  Well, you could build a dashboard with it and eyeball it, but that's not very satisfying -- sometimes you just want cold, hard numbers.  The good news is that there is in fact a way to do it using some new capabilities of custom summary fields.

    First, start by making yourself a standard Cases By Origin report by creating a custom report.  Choose the Customer Support Reports category, and then choose the first option, Cases -- we don't need any related objects for this report.  In the next step, make it a Summary Report.

    Now, here's the real trick.  The next step you'll see is the Select Columns To Total step.  We'll need this step later, but we're not ready for it just yet -- so skip it.  Do nothing here right now.

    Go to the next step, Select Grouping, and set it to group by Case Origin:

    Grouping
    Great.  Press the Next button to save your grouping settings. 

    Now go back to the Select Columns To Total step by using the dropdown list at the top right of the screen.  All the way at the bottom of the screen you'll see the place where you can create a new custom summary field:

    Custom_summary_new 

    Press that New button.  Because we'd jumped ahead and set the grouping, we now see a useful item here -- we can actually create a custom summary field per grouped item! 

    Summary field 

    This type of field will allow us to do math using both a summary of the group items and a summary of the whole report -- that's exactly what we need to calculate our percentages.  First, at the top of the page, give the formula field a name, and set its format to Percentage.  Now let's go to the formula builder and build it up.

    In order to calculate the percentage for each group, we need the record count for that group, and then we have to divide that by the total number of records in the report.  To get the record count for the group, we use the handy-dandy "Insert A Summary Field" dropdown.  That inserts an item that says "RowCount."  That was easy enough.

    Now we have to divide that value by the record count of the entire report.  For that, we can use one of the Custom Summary Functions -- to view them all, click the Show Functions >> link.  Here we see a function called PARENTGROUPVAL.  This is exactly what we need, because in this case the parent group is the entire report.  Our only grouping summary is Grand Summary, which is fine because that's what we want.

    Double-clicking PARENTGROUPVAL adds the following to our report:

    PARENTGROUPVAL(summary_field, GRAND_SUMMARY)

    Now we just have to select the summary field.  Select that "summary_field" part of the above, and replace it with RowCount, so it says:

    PARENTGROUPVAL(RowCount, GRAND_SUMMARY)

    So your final formula should read:

    RowCount/PARENTGROUPVAL(RowCount, GRAND_SUMMARY)Finished_formula

    Click on the image in the box to the right for a screenshot of the finished formula.

    Save your custom summary formula field, and proceed with the report wizard as usual, selecting and ordering your columns and entering criteria.  When you run the report, you'll see that it's grouped by Case Origin, and each group is tagged with the percentage of total cases that came from that origin:

    Finished_report

    Eureka!

  • Autocreating A Contact From Web To Case Or Email To Case

    Marco Casalaina Mar 11, 2009

    Today's post is not for everyone.  There's a very good reason why Salesforce.com does not autocreate contacts from Web To Case or Email To Case, and that reason is that one generally does not know enough about the person to associate him or her to an account.  "Can't you just use the domain from the email address?" people often ask me, and the answer is no.  If I get an Email To Case from somebody@yahoo.com, do I assume that person works for Yahoo?  Probably not.

    That said, there are Salesforce.com users who legitimately need to autocreate contacts in this way.  If you're one of those users, then today's post is for you.  Even if you're not, the trigger in today's post shows some solid techniques for bulkifying triggers, so if you intend to write Apex triggers, read on anyway.

    The best way to autocreate contacts from cases like this is to use an Apex trigger.  I'll post a link to the code here and then I'll explain what I've done.

    Here's the code and the corresponding test class.  Be careful when copying and pasting that code to your own instance of Salesforce.com, as these blog pages sometimes insert line breaks in strange places.

    So how does this work?  Let's examine the code a bit.

    First of all, whenever you're writing Apex, you must bear in mind that these cases may be submitted in bulk, as many as 100 at a time.  As such you have to be very careful about where you do your queries -- for example, it's rarely a good idea to do a query inside of a loop!

    Web To Case and Email To Case both use the SuppliedEmail field (that's the API name of that field -- you can see it on your Case Page Layout as "Web Email") to try to identify the person who submitted the case.  If the SuppliedEmail does not match the email address of any known contact in the system then Web To Case and Email To Case will leave the Contact field unfilled -- that's where our trigger comes in.  There's another field called SuppliedName that stores the name the person entered for himself (or the name associated with the email address).  We'll need that too.

    So the first thing we do is make a list of the SuppliedEmail email addresses in the cases that have just been submitted:

        List<String> emailAddresses = new List<String>();
        //First exclude any cases where the contact is set
        for (Case caseObj:Trigger.new) {
            if (caseObj.ContactId==null &&
                caseObj.SuppliedEmail!='')
            {
                emailAddresses.add(caseObj.SuppliedEmail);
            }
        }

    Having done that, we can now find any of the email addresses that already exist.  Why would we do that, you might ask?  If the email address already exists as a Contact, wouldn't Web To Case or Email To Case have already associated it?  Good question, there, reader.  You're using your noodle.  Normally, yes -- unless the email address is associated to more than one Contact!  In that case the system doesn't know which contact to choose, and so it doesn't associate any contact to the case.  We have to weed out any of the emails that are associated to multiple contacts, because this trigger also has no idea which one to associate to, so we'll leave those cases alone:

        //Now we have a nice list of all the email addresses.  Let's query on it and see how many contacts already exist.
        List<Contact> listContacts = [Select Id,Email From Contact Where Email in :emailAddresses];
        Set<String> takenEmails = new Set<String>();
        for (Contact c:listContacts) {
            takenEmails.add(c.Email);
        }

    OK, that's done.  Note what I didn't do there -- I didn't do that query inside the loop of cases coming up, but rather I did it outside the loop, using the in operator in my query.  That's good Apex practice.

    Another thing you don't want to do inside a loop is a DML operation -- inserts, updates and deletes.  I will momentarily be updating a bunch of cases, but it's a no-no to do that inside my upcoming loop, so I'll make a list of cases to update, and the contacts I'm going to insert, and put my cases in that list (and contacts in a map) so I can insert the contacts and update the cases all in a couple of bulk statements later:

        Map<String,Contact> emailToContactMap = new Map<String,Contact>();
        List<Case> casesToUpdate = new List<Case>();

    Good then.  Now I only want cases that have no contact associated to them, but have a SuppliedEmail and a SuppliedName (because there's no sense making a new contact with no name), and that SuppliedEmail isn't associated to multiple contacts, so:

        for (Case caseObj:Trigger.new) {
            if (caseObj.ContactId==null &&
                caseObj.SuppliedName!=null &&
                caseObj.SuppliedEmail!=null &&
                caseObj.SuppliedName!='' &&
                !caseObj.SuppliedName.contains('@') &&
                caseObj.SuppliedEmail!='' &&
                !takenEmails.contains(caseObj.SuppliedEmail))

    In the loop, I take each case's SuppliedName and split it by the space.  This works reasonably well for 2-word Anglican and Latin names, but it can produce a funny looking name if, for example, somebody enters his name like "Marco S. Casalaina."  If you want better looking names, feel free to tweak the logic here. What can I say, I'm just lazy!

    With our name nicely formatted, we can create a spartan contact which will have nothing but a name and an email address, since that's all we know:

                //The case was created with a null contact
                //Let's make a contact for it
                String[] nameParts = caseObj.SuppliedName.split(' ',2);
                if (nameParts.size() == 2)
                {
                    Contact cont = new Contact(FirstName=nameParts[0],
                                                LastName=nameParts[1],
                                                Email=caseObj.SuppliedEmail,
                                                Autocreated__c=true);
                    emailToContactMap.put(caseObj.SuppliedEmail,cont);
                    casesToUpdate.add(caseObj);
                }

    Now leaving the loop, we're good to go -- we can create our contacts now:

        List<Contact> newContacts = emailToContactMap.values();
        insert newContacts;

    Now that we've inserted the contacts, they're "real" and have IDs, so we can associate the cases to them:

        for (Case caseObj:casesToUpdate) {
            Contact newContact = emailToContactMap.get(caseObj.SuppliedEmail);
            
            caseObj.ContactId = newContact.Id;
        }

    And that's it!

    So, you ask: where's the update statement for the cases?  Wow, reader, you do have a sharp eye.  We don't need one!  This code is running in a before insert trigger on Case.  Since the cases in this list are the ones that actually fired this trigger, if I modify them here, I don't need to call update on them -- I just have to change their field values and they will automagically be updated.

    The code given here is by no means perfect, and as I've said, it's not for everyone.  Some people may want to modify this to create a Person Account instead of a Contact; others may want to tweak how I'm splitting the name, since as I said before, I was a bit lazy with it.  But this code does demonstrate two things.  First, it shows how to autocreate contacts, as the title of this blog indicates.  More importantly, though, it's shows some techniques for writing triggers that behave properly in bulk.

    As Martha Stewart would say, "Bulkified triggers -- they're a good thing."

  • Ringing In Success With Telephony Integration

    Marco Casalaina Mar 5, 2009

    Did you know that you can integrate Salesforce.com with your telephone system?  Many call centers are doing just that these days to lower their average handle times and improve customer satisfaction.  This can be achieved through the magic of computer-telephony integration -- commonly known as "CTI."  For some time, Salesforce.com has provided a CTI Toolkit to our extensive network of telephony partners that allows them to build integrations between their telephony platforms and Salesforce.com.

    In the Spring '09 release, we added Firefox support to our CTI Toolkit, which is why I chose today to write about CTI.  But while we're on the topic, let's do a quick recap of what CTI integration with Salesforce.com is, how it works, and how you can get started with it.  Let's cover some of the frequently asked questions.

    What is a CTI integration with Salesforce.com?

    A CTI integration, otherwise known as a CTI Adapter, is actually a small executable, provided by one of our telephony partners, which sits on each agent's desktop.  When you have a CTI adapter, you get the following benefits:

    Screen pops on incoming calls -- When a call arrives at your phone, the CTI Adapter will initiate a search in Salesforce.com based on the caller's phone number or on digits he may have entered via the IVR (the Interactive Voice Response system which might ask you questions like "Please enter your account number").  If it finds a match, it will pop a Salesforce.com page showing the contact or account record of the caller, for instance, or a different object if you configure it to do so.

    Click-to-dial  -- When a CTI Adapter is present, all phone numbers in Salesforce.com become clickable links -- you need only click them to dial those numbers.

    Automatic call logging -- When you are on a call, you'll have the opportunity to take notes on that call, which will be automatically logged as an Activity and associated to objects that you have navigated to during the call (for instance, the caller's contact record, or a Case or Opportunity you created during the call).

    Call control from within the Salesforce.com web interface -- You can make calls, hold calls, transfer and conference without leaving Salesforce.com and without touching your phone.

    Here's a recorded demo of a CTI integration in action.  For you long-time blog readers out there, yes, that's me narrating it, so you finally get to hear my voice :).

    Who makes these integrations, and what telephony platforms are supported?

    CTI adapters are made by our partner ecosystem.  Salesforce.com itself does not make any of these integrations -- we leave that to the telephony experts.

    As of this writing there are integrations to over 80 different telephony platforms, including all the major on-premise ones like Cisco, Avaya, Genesys, Nortel, Shoretel, Interactive Intelligence, and many more.  You'll also find integrations to many of the cloud-based telephony providers like LiveOps, inContact, Fonality, and Five9.  Therefore there's a very good chance that your telephony system is supported.

    Does it cost anything?

    Salesforce.com does not charge for our CTI integration capability -- it's included.  However, as I mentioned above, you'll also need the CTI adapter from one of our partners in order to actually connect Salesforce.com to a telephone system.  Some of our partners give their adapters out for free with the purchase of their products, and some partners charge for their adapters, so there may be a cost involved in obtaining an adapter depending on which telephony platform you're using.

    How do I find out if my specific telephony platform is supported?

    The majority of our partners who have built integrations to Salesforce.com list them on the AppExchange -- so step 1 would be to go to the AppExchange and search the maker of your phone system.  Don't search the whole system name -- if you have Cisco UCC Enterprise 6.0, just search "Cisco", for example -- and chances are you'll find what you're looking for. 

    Sometimes you'll find multiple different vendors who provide integrations for the same platform.  That's the free market at work!  Because they're all using the Salesforce.com CTI Toolkit, the integrations all look the same (they'll all look like the recorded demo above), but different vendors built their integrations in different ways.  Some integrate directly to the telephony system, others use a middleware appliance.  Sometimes there's a cost difference between them, and sometimes your IT department will have a pre-existing relationship with one of the vendors.  When there is more than one vendor providing a CTI adapter for your platform, you win -- you get to choose which suits your needs the best.

    If you don't find it on the AppExchange, ask your Salesforce.com account executive.  There are some vendors who did not list their integrations on the AppExchange, but integrations still exist for their platforms.

    What editions of Salesforce.com do CTI adapters work in?

    Generally, the answer is "Enterprise Edition and up."  However, some vendors who have passed the AppExchange "Security and Trust" certification are able to provide adapters which also work in Professional Edition.  You'll know who those vendors are because they'll have a little Cert_salesforce[1] icon on their AppExchange listings.

    Do you have a simulator or something that we can try out?

    Yes.  Salesforce.com offers the Demo Adapter, which simulates much of the functionality you would get with a real adapter, but does not actually connect to a telephone system.  If you have at least Enterprise Edition and you have admin privileges to your Salesforce.com org, you can download it and try it out by following the instructions here.

    Can my company build its own CTI adapter?

    It's not recommended.  Building a new CTI adapter requires a great deal of telephony expertise, and every telephony platform has little idiosyncracies.  You'd really need to have an experienced development team devoted to this task.  We'd strongly recommend you look for a prebuilt adapter rather than writing your own.

    My company makes telephone systems.  Can I build an integration to my company's telephony system?

    Yes!  You can build it using the CTI Toolkit.  When your integration is complete, we'd recommend you list it on the AppExchange so that Salesforce.com customers can find it easily.

  • Reporting On "Cases Filed By My Accounts"

    Marco Casalaina Mar 2, 2009

    The other day a poster on the Best Practices board asked whether there was a way to filter a report on Cases by "My Accounts."  By default, Case reports are filterable by the case owner, but not by account.  Fortunately, there is a way to do this, it's just a little roundabout:  you have to create a case report which is driven off of Account using Custom Report Types.

    1.  Go to Setup->App Setup->Create->Report Types.

    2.  Make a new Custom Report Type.  Select the primary object as Account.

    3. On the next screen set the "B" object to Cases -- I use "with or without cases" so you can see accounts of yours that have no cases on them.

    4.  Save it.  Now go to the reports tab and make a report of this type.  When you get to the filter part you'll see that you can now filter by "My Account." 

    You can also add regular criteria to case fields like Case.IsClosed so that you only see the open cases in the report, for example.