Archive for April, 2008

Really? You don’t like torture, Antonin? Methinks you do.

Monday, April 28th, 2008

These are the people in positions of power in this country. These are the protectors of our constitution. I am dumbfounded. Is this the interpretation of the rules of our society that you want to live under? I take great solace knowing that if I am ever tortured by our government, something that would be legal today should Bush choose to torture me, it won’t be considered punishment under the law. And they call liberals ‘radicals’.

NPSF group reached 300 members today

Friday, April 25th, 2008

The nonprofit Salesforce.com implementer community put up shop over on Google groups about a year and a half ago. Today we reached 300 members. If you’re interested in Salesforce.com and nonprofits, or just want to lurk on the best damn Salesforce.com implementer forum out there, feel free to join.

Macs at One Market?

Friday, April 25th, 2008

A rumor is bouncing around that Salesforce.com Corporation is thinking about switching from Dell to Apple for all their desktops. Seems a bit far-fetched to me, but not impossible.

I think it’s far fetched because power users of Salesforce rely heavily on three things:

  1. Awesome utility from a MS Outlook inegration
  2. Dynamic spreadsheets with MS Excel integration
  3. Mail merge integration with MS Word

I can’t believe that Salesforce.com would tell it’s staff that these tools will no longer work for them.

If this rumor is true, and not just a way for Marc to negotiate a better deal with Michael, look for:

  1. Integration with Thunderbird to get real–Steven has been doing this project in his spare time
  2. Mail.app integration may ge beyond what Simon has built for his own use
  3. Mail merge integration with Google Docs or Mac Word
  4. Dynamic reports in Mac Excel–google spreadsheets just aren’t powerful enough yet

Apex to Auto Convert Leads

Thursday, April 24th, 2008

I’ve got a client who gets a lot of leads and cases through the web. I’ve created some auto-conversion apex to simplify the amount of dupe checking they need to do:

Vertical Response: free emails for Nonprofits

Thursday, April 24th, 2008

We’ve been liking Vertical Response lately for it’s really nice integration with Salesforce.com, and we just started liking it more. They just announced that nonprofits using Vertical Response will get 10,000 sends a month for free!

That means that most of ONE/Northwest’s customers won’t be paying anything at all for sending blast emails directly from Salesforce.

Thanks Vertical Response!

Trigger to Create Contact from a web-submitted Case

Tuesday, April 22nd, 2008

I’m building an online request system–it’s basically forms that do what web-to-case does, but without the web-to-case.

One thing I wanted for my system was a way to create Contact records for people submitting Cases if I haven’t seen them before, and use their existing Contact record if I have seen them before. I talked a bunch with Kathy Dunne and even got a look at some code she deployed for a client.

My needs were a bit different, so I thought I’d post the code. In short, this is what it does:

  • The trigger doesn’t fire on bulk loads, like data loader loads
  • On Case creation, the trigger fires and checks to see if the case came from the web
  • It looks for a Contact with the same email as was provided on the Case, if it finds it, the Case is then related to that Contact and that Contact’s Account.
  • If a Contact match wasn’t found, it tries to match the Account Name with the Supplied Organization Name. If a match is found, the Contact is created in that Account, and the Case is related to the Account and the new Contact.

Here is the Case Trigger, it breaks the page, but better to see the white space than have the page look nice:

// Written by Steve Andersen, copyright (c) 2008 ONE/Northwest
// This program is released under the GNU General Public License. http://www.gnu.org/licenses/
trigger ONEN_CreateContactFromCase on Case (before insert) {
	//only fire when the batch size is one. Should only be relevant when a case is created from the website
	if (trigger.new.size() == 1) {
	 	//process the created lead
		for (Case createdCase : Trigger.New) {
			//variable to hold the New Account Id
			Id newAccountId;
			//if an email is given, process the Contact info
			if(createdCase.SuppliedEmail!=null) {
				//look for a Contact that matches the email address
				Contact[] contactsMatched = [Select Id,AccountId,email from Contact where email =:createdCase.SuppliedEmail LIMIT 1];
				//if we found a match, use it
				if (contactsMatched.size()>0) {
					createdCase.ContactId = contactsMatched[0].Id;
					createdCase.ContactId = contactsMatched[0].AccountId;
				} else {
					//if the Company name was supplied, match it or create a new one
					if (createdCase.SuppliedCompany!=null) {
						Account[] accountsMatched = [Select Id from Account where Name =:createdCase.SuppliedCompany LIMIT 1];
						//if a match was found, use it
						if (accountsMatched.size()>0) {
							newAccountId= accountsMatched[0].Id;
							createdCase.AccountId= newAccountId;
						} else {
							//create a new Account
							Account createdAccount = new Account();
							createdAccount.Name = createdCase.SuppliedCompany;
							createdAccount.BillingStreet = createdCase.SuppliedCompanyAddress__c;
							createdAccount.BillingCity = createdCase.SuppliedCompanyCity__c;
							createdAccount.BillingState = createdCase.SuppliedCompanyState__c;
							createdAccount.BillingPostalCode = createdCase.SuppliedCompanyPostalCode__c;
							insert createdAccount;
							newAccountId = createdAccount.Id;
							createdCase.AccountId = createdAccount.Id;
						}
					}
					//create the contact
					Contact theContact = new Contact();
					theContact.FirstName = createdCase.SuppliedFirstName__c;
					theContact.LastName = createdCase.SuppliedLastName__c;
					theContact.AccountId = newAccountId;
					theContact.Email = createdCase.SuppliedEmail;
					theContact.Phone = createdCase.SuppliedPhone;
					theContact.MailingStreet = createdCase.SuppliedCompanyAddress__c;
					theContact.MailingCity = createdCase.SuppliedCompanyCity__c;
					theContact.MailingState = createdCase.SuppliedCompanyState__c;
					theContact.MailingPostalCode = createdCase.SuppliedCompanyPostalCode__c;

					insert theContact;
					//set the contact Id on the case
					createdCase.ContactId = theContact.Id;
				}
			}
		}
	}
}

And here is the Test:

// Written by Steve Andersen, copyright (c) 2008 ONE/Northwest
// This program is released under the GNU General Public License. http://www.gnu.org/licenses/
public class ONEN_TEST_ContactFromCase {
	static testMethod void TestCreateCase() {	 

		//create first case
		Case firstCase = new Case (
			Subject='Test Case',
			SuppliedFirstName__c='Joe',
			SuppliedLastName__c='Johanssen',
			SuppliedEmail='joe@email.com',
			SuppliedCompany = 'Test Company',
			SuppliedCompanyAddress__c = '1234 Elm St.'
		);
		insert firstCase;

		Case thisCase = [select AccountId,ContactId from Case where Id =:firstCase.id];

		//the Company and Contact Ids should not be null
		System.assertNotEquals(null,thisCase.AccountId);
		System.assertNotEquals(null,thisCase.ContactId);

		Account thisAccount = [select Name,BillingStreet from Account where Id=:thisCase.AccountId];

		System.assertEquals('Test Company',thisAccount.Name);
		System.assertEquals('1234 Elm St.',thisAccount.BillingStreet);

		Contact thisContact = [select FirstName,LastName,Email,MailingStreet from Contact where Id=:thisCase.ContactId];
		//the name should be as expected
		System.assertEquals('Joe',thisContact.FirstName);
		System.assertEquals('Johanssen',thisContact.LastName);
		System.assertEquals('1234 Elm St.',thisContact.MailingStreet);

		//create second case
		Case secondCase = new Case (
			Subject='Test Case',
			SuppliedFirstName__c='Joe',
			SuppliedLastName__c='Billings',
			SuppliedEmail='joe@email.com',
			SuppliedCompany = 'Test Company'
		);
		insert secondCase;

		Case thisSecondCase = [select AccountId,ContactId from Case where Id =:secondCase.id];

		Contact thisSecondContact = [select FirstName,LastName,Email from Contact where Id=:thisSecondCase.ContactId];

		System.assertEquals('Joe',thisSecondContact.FirstName);
		System.assertEquals('Johanssen',thisSecondContact.LastName);
		//we should not have created another contact
		System.assertEquals(thisCase.ContactId,thisSecondCase.ContactId);

	}
}

You’ll see there are some custom fields required in the code. Here is an Appexchange package of those fields.

We create these forms with the awesome PloneFormGen and the SalesforcePFGAdapter to get data into Salesforce. PloneFormGen has an automatic CAPTCHA field, so we’ll likely protect ourselves from spam by requiring CAPTCHA on all web submitted Cases.

I’m really loving working with Plone and Salesforce on this project. The tools are coming together nicely and I’m really excited about the potential of this integration between the best open-source CMS and the best on-demand CRM.

Workout Plan in Google Spreadsheet

Monday, April 21st, 2008

That’s a summary of my triathlon training plan for 2008 published as a Google gadget. I built a Google spreadsheet to plan my year, based largely on Joe Friel’s methodology.

I estimate my weekly hours, marking the events I want to race. Then breakdown the weekly estimates into real workouts. Then track the actual workouts.

If you want to build your own plan, I’ve published a copy of it here. Take it and make it your own!

Update: Here’s an XLS download as well.

Sell MSFT…before it’s too late

Wednesday, April 16th, 2008

Google todo lists

Friday, April 11th, 2008

TechCrunch posted a reader tip that a Google todo list product may be coming soon. Arrington says,

But really, how much can really be done to create an even better way of creating a list, organizing it, and removing finished items? Perhaps this is yet another niche market that Google intends to dominate, and I’m not sure anyone will really care.

But astute readers will link this to the Salesforce-Google announcement to come next week and might see a todo list product as the only thing missing from Google apps to allow for deep syncing of Salesforce information. Contacts to Gmail, Events to Gcal, and perhaps, Tasks to Google TooDoo.

Obama, the CRM candidate

Wednesday, April 9th, 2008

Brent tipped me off to an article about Barack Obama’s use of CRM in his run for President. From the article:

One tool they implemented was an email response system, Invite Barack, in two weeks to handle non-(news) media requests for Barack or members of his campaign to attend local events. There is an online form to fill out which gets into a work cue with a team assigned to properly acknowledge and respond to the request. This helped streamline non-media requests to ensure nothing got lost, all requesters were acknowledged, and responded to quickly. In addition, the system allows the campaign to monitor trends in requests such as location, requesting organizations, outcomes, etc.

I’m going to build something similar for Green for All, albeit on a slightly smaller scale…