Autocreating A Contact From Web To Case Or Email To Case - Ideas - Salesforce Trailblazer Community
Trailblazer Community

All Ideas

Idea Details

Post an Idea
470  Points
Open
Idea has been posted. Give it an upvote or downvote.

Autocreating A Contact From Web To Case Or Email To Case

Accounts & Contacts

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.

The code and the corresponding test class is here, on the Developing With Service Cloud wiki.  Be careful when copying and pasting that code to your own instance of Salesforce.com, as these wiki 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."

 

· Flag

  • Upvotes
  • Downvotes

Ideas

Apps

from AppExchange

Help us to keep IdeaExchange clean by pointing out overlapping ideas. We'll investigate your suggestion and merge the ideas if it makes sense.



 

 

Thanks for your merge suggestion. We will review it shortly and merge the ideas if applicable.

Salesforce takes abuse situations very seriously. Examples of abuse include but are not limited to posting of offensive language or fraudulent statements. To help us process your request as quickly as possible, please fill out the form below describing the situation. For privacy and security reasons, the final outcome of an abuse case may not be revealed to the person who reported it.


 

Thank you for your feedback. We take abuse seriously and will investigate this issue and take appropriate action.