Archive for March, 2007

I knew it had to happen eventually

Tuesday, March 13th, 2007

After 2 years of daily bike commuting in Seattle, I was finally hit by a car today. I’m a bit sore, but fine. I was riding over to NPower Seattle to get a lesson in migrating data out of eTapestry from the incomparable V Waters when it happened. I was riding in the bike lane going south on 2nd avenue when a man swerved right into me as he pulled over to pick up his wife at the curb. I saw him coming but didn’t have time to do anything but break.

He hit my right side and sent me skidding. I then fell over backwards and landed in the open parking space he was headed for. My elbows hit (ow) and my helmetted head hit the pavement, but not very hard judging from the light damage to my lid.

Like most bike accidents, I was obeying traffic laws and the car driver was completely at fault. That doesn’t get me anything, unfortunately. Somehow in the accident my chain broke, but the rest of my ride appears in good shape.

I was definitely shaken, though. My first reaction was anger, then I was stunned for at least 4 hours. Pretty unsettling, even though I didn’t really get hurt. I headed back to the office and just started working again. It was a bit surreal.

So to all the drivers out there, Watch out for bikes! And to all the bikers out there, The drivers aren’t watching out for you!

Making CampaignMember customizable

Tuesday, March 13th, 2007

I have been talking a lot about the Campaign infrastructure in Salesforce.com lately. One of the things I really want (aside from Sharing Rules) is the ability to add fields to the CampaignMember table. This is the table that tracks the people attending the event, or being sent the direct mail piece.

It would be easy to allow us to add more fields to that table–Salesforce.com is very good at that. I think the challenge comes in how to manage the display of those customizations. And here’s where I wanted to throw an idea out and see what people thought.

Out of the box, you could create new fields on CampaignMember. Then you could modify the CampaignMember Page Layout to control how those records look. That’s pretty straightforward. And since we’ve got a Page Layout, I’m implying that we’re able to view individual CampaignMember records, which means we should have a Related List on the Campaign.

So if I’m having my yearly auction dinner and I need to track membership, I can create a picklist for entre, and I can use that data to inform the caterers. I could even set up a web to lead form because Lead mapping would let me pass that data through to the CampaignMember table.

But when I want to do direct mail, I don’t care about entre. This is where Record Types could come in. We could Create a Campaign Record Type for Direct Mail, and then tie that to a Campaign Member Record Type. Then, all the Direct Mail Campaigns would have CampaignMembers with Direct Mail fields, which could be controlled by a specific page layout. All my Dinner Record Types would have entre as a field on the CampaignMember Page Layout.

This gives groups lots of flexibility around have simple and complex events. Just a thought that has been rattling around in my head. The Salesforce.com folks probably have a better concept than this on their roadmap somewhere, and I can’t wait to see it.

Google Analytics: Hey, I was using that!

Sunday, March 11th, 2007

I logged into my Google analytics account the other day to discover that my www.gokubi.com profile was no longer there. I contacted Google to get the scoop. Their tech support told me via email that my gokubi.com profile had been deleted. And that the only one who could have deleted my profile was me.

Hmmm. I don’t remember deleting my profile. I usually remember those kinds of actions, what with the “Are you sure you want to do this action that can’t be undone?” confirm message that usually accompanies them.

I responded that I wanted to know how this happened since I didn’t delete my profile. They closed the case and sent me a customer satisfaction survey. I guess they’re done with me.

So, a year of stats down the drain. And no recourse because it’s a free offering. I’m not sure if I’ll set up a new profile for www.gokubi.com with Google Analytics. The functionality is great, but I’m a bit annoyed that they killed my profile and don’t seem to care.

And it makes me think twice about my business use of Google Spreadsheets. Could I bounce back from a deletion of those files? Surely not as easily as I can bounce back from losing my blog stats. On-demand services that have no price tag associated with them are a bit scary. I love using free services, but when they turn on you, there’s not much you can do about it.

Using one Salesforce.com instance for two organizations

Sunday, March 11th, 2007

Sometimes an organization needs to segment it’s data very clearly and essentially have two organizations using the same database. There are often good reasons where one organization needs a bright line between two divisions. This can be accomplished in Salesforce.com. Here’s what I’m building for a group I’m working on right now.

If you see any gaps in what I’m doing, I’d love to hear your comments. I haven’t had to build much security and sharing into my Salesforce.com implementations yet, so I’m surely no expert.

Set default sharing settings to Private
If you want to restrict access to data in Salesforce.com, you start by tightening things all the way down and then loosening them up where it makes sense to your model. By setting sharing to Private, we’re saying you can only see things you own.

Set up a Role Hierarchy that has two branches
Roles control what data Users can see, so we’ll use them to split the data down the middle. Right below the top level of the Role Hierarchy, create a Role for Department 1 and a sibling Role for Department 2. All Users will live in one of these two branches of the hierarchy.

Set Object-specific sharing rules for each branch of the hierarchy
For each object (like Account) create a rule that gives read-write access to all objects owned by Users in the Department 1 Role Hierarchy to all other Users in that branch of the hierarchy. You’re basically saying, share all my stuff with my side of the org, and they will do the same. Mirror that for the Department 2 side of things, and on all objects you care about.

Create Users, placing them in the right Roles
All Users must be in the Department 1 or Department 2 side of things. Systems Administrators must be in one of the two roles as well.

Create Profiles for the two sides
Create a Department 1 Standard User Profile, and a Department 1 System Administrator Profile. Do the same on the Department 2 side. We need the systems administrators for the Department 1 to only see Department 1 data, so we also have to remove their permission to “View All
Data.” This will only allow them to see the Department 1 data, whether it be in
the web interface, or when using the API. Do the same for the Department 2 sys admins.

Deal with Campaigns
As I’ve written before, Campaigns aren’t a first-class object, so you have to hack the data split a bit. Here’s what I thought I would do. Create two Record Types for Campaigns–Department 1 Campaign and Department 2 Campaign. On all the Profiles you use for the Department 1, make the Department 1 Campaign the only available record type. Do the reciprocal on the Department 2 profiles. But so far I haven’t been able to restrict visibility to the Campaign Record Types of the other side. I’d love to hear any thoughts about this one.

Like I said, I’m no expert on this. It seems like we’re very close, except for the Campaign piece. Is there any way to make Campaigns behave?

Progress on Appexchanging record types

Thursday, March 8th, 2007

I wrote the other day about how I was being stymied by the Winter ‘07 Appexchange. In my post I never used the word bug, because I didn’t really see the issues I was having as technical flaws in their Appexchange implementation, but more a disconnect between my process and the tool. I’m happy to say that with a nudge in the right direction from Salesforce.com (more bout that later), I was able to overcome the Record Type issue and found a possible solution to the custom field problem.

If you type Record Type Ids in your code, the Appexchange validator picks them up and won’t let you continue. In the past we were just storing those Ids in the code and changing them when we switched instances. It was quick and dirty and worked with the old Appexhcange. I wrote a simple function that will pull those Ids programatically and it doesn’t cause the validator halt.

	var DonationLabel = "Gift";
	var MembershipLabel = "Membership";
	var oOppRecordTypes = getRecrodTypesforObjects("Opportunity");
	var DonationRecordType = oOppRecordTypes[DonationLabel].Id;
	var MembershipRecordType = oOppRecordTypes[MembershipLabel].Id;

	function getRecrodTypesforObjects(ObjectName) {
		var RecordTypesArray = new Array();
		sfc = sforce.connection;

		var result = sfc.query(”Select r.Id, r.Name, r.SobjectType from
		RecordType r where r.IsActive=true and r.SobjectType=’”+ ObjectName +”‘”);

		var RecordTypesArray = result.getArray(”records”);
		oRecordTypes = new Object();
		for (var i=0;i<RecordTypesArray.length;i++) {
			var oSingleRecordType = new Object();
			oSingleRecordType.Name=RecordTypesArray[i].Name;
			oSingleRecordType.Id=RecordTypesArray[i].Id;
			oRecordTypes[RecordTypesArray[i].Name]=oSingleRecordType;
		}
		return oRecordTypes;
	}

We’re getting all the Record Types on an object and calling them by name. This has the added benefit of needing no touches if we switch instances and the Record Types are named correctly. Hooray!

I also noticed as I loaded this code to the Appexchange that the 10 custom fields I am calling were not seen as references by the validator. I think this is because the field names are created on the fly–they are names like MembershipFY2006, MembershipFY2007, etc. So we may be able to fool the validator by using variables whenever we’re referencing custom fields. I’ll have to try some tests to find out just how smart the validator is. So this may be a hooray as well. I’ll reserve judgement until further testing.

So the Appexchange isn’t really crippled, it’s just that changes I didn’t understand happened to the platform and my previous work processes were foiled by those changes. I may be able to get back to full functioning on the Appexchange with a couple changes to process and coding style. That would be great.

It’s also been a very real example of the challenges of relying on technology to get our work done. As technology changes the way we work often needs to change. Sometimes subtly, like with my new routine for getting Record Types. Sometimes the changes are really big, like when you go from Excel for tracking donor prospects to using a full-fledged donor management system. Change is hard, and hardest when you don’t see it coming and it forces you to change the way you work, even if the new way of working is better!

Some advancements in multi-payment gifts

Thursday, March 8th, 2007

I carved out some time this weekend to build an Scontrol that takes a single Opportunity and breaks it into multiple Opportunities at user chosen intervals. I’m really happy with it–I like the way the UI turned out.

payment_sm.png

You can take any Opportunity and turn it into x other opportunities spaced y interval apart. It takes care of the math for you, and sets default name, stage, date, and amount. But the coolest thing is that you can then modify all that info before creating anything. So if someone is making monthly gifts but is going to front load their first gift you can do that easily. Or if a grantmaker is giving you $100,000 over 4 years but not $25,000 a year, you can do that. And then it will create a prospecting Opportunity at the end of the cycle for the full amount–in 4 years you want to remember to apply for another grant.

But really the coolest thing is how this ties into online donations at an external site. Say you create 12 monthly Opportunities for someone. And say they are set up at your online donation service to have their credit card processed every month. You can download the monthly transactions from your service and import them to Salesforce.com as Leads and use the Lead Converter to process them. And over the weekend I modified the Lead Converter to allow you to merge gifts to existing Opportunities.

leadconversion_sm.png

This makes handling monthly giving via PayPal or Network for Good very easy. Create all your Opps in Salesforce.com via the new Opportunity splitter, then merge the payments to the Opps when they come in. Finally I am happy with how I can support monthly giving in Salesforce.com. It’s been a couple years coming.

links for 2007-03-08

Thursday, March 8th, 2007

Appexchange crippled in Winter ‘07

Tuesday, March 6th, 2007

In Winter ‘07 Salesforce.com had a major rev, and we got a lot of things that are really helpful. A couple stand outs:

  • The ability to use includes in SControl (custom code) development
  • Sharing custom fields on standard objects via the Appexchange
  • Creating full-fledged UI buttons

I’ve been loving these new features since I got them, doing all sorts of new things which have significantly improved the user experience inside the databases I’m building.

But I’ve come to realize that some low-profile Winter ‘07 functionality is having a big effect on how I work. It’s making it much more difficult to do Salesforce.com SControl sharing via the Appexchange. I’m talking about Appexchange package validation.

When you package up a UI button for sharing, Salesforce.com is smart and realizes that the Button requires the SControl, which may require an SControl snippet (included library) and may make queries that call a custom field on a standard object. All these items are added to the package as required, and can’t be removed unless the Button is removed. They are treated as dependencies.

But when you go to install an Appexhange package, it tries to install these dependencies even if you already have them. And if you already have them, the install fails. You can’t install the package.

Here is why this is a problem. First, by using includes, I can reference existing SControl libraries. I have a Utilities library that is included in most of my custom code. If I want to package 3 different pieces of my app in different packages, they will all require the Utilities library, and won’t install because of conflict.

Second, we’ve gotten together in the nonprofit community and worked with the Salesforce.com Foundation to identify a set of objects and fields that are common to nonprofits using Salesforce. So a lot of the people I want to share code with already have a Fund field on their Opportunity object. This is great–the commonality helps us share. But in this case the commonality causes the Appexchange installs to fail. They already have the fields that are required for my code, but the install fails because they already have the fields that are required for my code. Arg!

Third, Salesforce.com uses record types on objects as a really cool way to extend customization ability. I use it a lot, especially for Opportunities. The new package validation inspects your code for references to record types. It appears to even find Salesforce.com GUIDS in hard coded variable definitions. And when it finds them, it won’t include the code in a package because record types can’t currently be Appexchanged. So any code that references record types can’t be appexhanged at all.

Sharing of code from one instance to another is now back to way I did it before Winter ‘06–copy and paste text files. There are very few things I can Appexchange without problems. It makes doing consulting on the platform more expensive, and it makes sharing with colleagues and the broader community more difficult.

To fix the situation, a couple of things need to happen. I’m all for a package containing everything it needs to work, but the installer has to be smarter. If a field that is part of the package already exists in the install destination, warn the user and then install the package. Maybe even check to make sure the field is the right type.

Next, the code library situation has to be figured out. How can you code with libraries and share them modularly? I don’t know. Perhaps on the fly renaming of snippets that run into naming conflicts? That would at least get the package installed, and redundancy could be cleaned up later.

Finally, record types should be ignored by the validator until there is a good way to handle them gracefully. I need to be able to refer to record type Id’s for my custom code to do anything helpful. If I can eventually Appexchange full record types, that’s great, but they have to gracefully abort if they already exist in the destination databass.

Maybe Spring ‘07 will fix some of these validation issues. I can imagine that Salesforce.com has some unhappy customers, especially if this behavior is being seen in their Sandbox product. People are paying money for that service which is supposed to make testing and installing code easier. I would be upset if I were paying for a product that lost functionality unannounced.

I have faith they will be fixed. I’ve been continually impressed with how Salesforce.com is moving the platform forward as the complexity continues to increase. All in all, Winter ‘07 is still way on the positive side of the scale for me, and would be off the charts if these problems hadn’t been introduced.

Steps for migrating people from a standalone email blasting tool to Salesforce.com and What Counts

Tuesday, March 6th, 2007

I recently moved a group from an email blasting tool to sf.com integrated with What Counts and took notes. I don’t know if anyone is interested, but here are the steps required:

  • Generate list of all subscribers and the lists they are subscribed to from the old system
    • email address
    • first name
    • last name
  • Use DemandTools Find Contact ID’s for known email addresses
    • exact match on email address
  • Update found Contacts with email subscription information
    • Mark a checkbox for the newsletter in question
  • Load all people without direct matches in Contacts as Leads
    • Process them with the Lead Converter to do matching and create Households and Accounts
  • Add people who didn’t have names in the old system directly to What Counts
    • They will get mailings, but won’t be synched with Salesforce.com
  • Set up What Counts to talk to Salesforce.com
    • Create an integration profile (username and password)
    • Tell What Counts which Contacts should be on each list
      • Schedule those pulls to run nightly
    • Tell What Counts what to do in Salesforce.com when a person unsubscribes from a list
  • Set up the Web To Lead for form getting new subscribers into Salesforce.com
  • Test
    • Web To Lead
    • Lead Conversion
    • What Counts pull
    • Unsubscribe

And just like that, you’re done!

links for 2007-03-04

Sunday, March 4th, 2007