Archive for Tips Category Feed

Getting Standard Object Meta Data into the Force.com IDE

Salesforce on Rails has an interesting post on how you can get the meta data for standard objects into the Force.com IDE for easy manipulation of its configuration via the Meta Data API capabilities built into the IDE.

Comments (1) comments feed

Best Thing about the Salesforce-Google Toolkit

As you probably know, Salesforce announced the Google Data API Toolkit on Monday.  Per the site,

The new Force.com Toolkit for Google Data APIs provides a free and open-source set of tools and services that developers can use to take advantage of Google Data APIs from within Force.com.

The end result for developers is a set of classes, written by Salesforce, that allow you to easily communicate with Google services.  For example, suppose you want to create an entry in Google Calendar, the following code does it.

GoogleData.Calendar cal = service.getCalendarByTitle('MyCalendar');
event newEvent = new Event(
subject = 'Tennis with Beth',
description = 'Meet for a quick lesson.',
ActivityDateTime = system.now(),
DurationInMinutes = 60);

That is actually just 2 lines of code (line #2 was broken into 5 lines for easier reading). The reason you can do this in 2 lines is because of the toolkit.  The toolkit does the “heavy lifting” for you to communicate with Google.

From a developer standpoint, the best thing about this is that, to do this, there is no dependency on changes to the Salesforce platform.  The Google Toolkit was created by the Developer Marketing Team at Salesforce, not the folks building the platform.  Apex Code is already part of the platform. The toolkit uses what Apex Code already offers.  What you get with the toolkit is a set of pre-written Apex classes that do the heavy lifting for you on talking to Google.  Much like the Salesforce Java/PHP/.NET/Ajax/Perl toolkits do the heavy lifting of talking to the SOAP API for you on those programming platforms.

There is no reason that the developer community cannot create similar toolkits.  I am sure Salesforce has some more up their sleeve and did the community a service by building some foundational ones for us to use as working/useful examples.

This is open source at work.  To make an analogy… one thing I love about PHP is that these types of toolkits are prevalent and have made my life much easier. For example, when I wanted to build Auto vCard, I Googled for PHP classes that created files in the vCard spec.  I found many and chose 1.  Similarly with the Web to Lead Spam Check I built.  I chose Akismet as the spam checking service because I was familiar with it and trusted it and I found a PHP5 toolkit that took care of the hard part of communicating with Akismet from PHP.  Same thing with my old Google Maps mashup. There are PHP classes that do the hard part of talking to Google Maps.  Having these classes at my disposal gave me the ability to focus on adding the business value of tying functionality into a Salesforce-related use case.  If these PHP classes didn’t already exist, I never would have created any those apps.

If you are a developer looking to create something similar to what Salesforce did, I suggest you:

  • Visit Programmable Web to identify useful services that could be connected to and learn about their APIs
  • Build your class(es) (Look at the XMLDom class that’s part of the Google toolkit to handle the complicated XML parsing you might need to do)
  • Publish it open source and let the community react/improve it
  • Offer up some example code for how the class can be used to help people implement a use case of it.
  • Become a star

Some services that I think are ripe for developers to concentrate on (that are very applicable to businesses):

  • Google Checkout
  • PayPal
  • Authorize.net
  • Freshbooks
  • Google (Apps) Mail
  • Google Charts (with tie into Visual Force)
  • Blog Services
  • UPS
  • FedEx
  • Google Search
  • Facebook
  • LinkedIn
  • Google Open Social

There are countless others.  Get cracking!

Comments (4) comments feed

Auto-Create Campaign Members for New Leads

After you finish reading this, check out a newer post that “bulkifies” the trigger.

I created a simple & very useful Apex script that will auto-create Campaign Members for me when new leads are entered manually or via Web to Lead IF the Lead Source value matches the Name of a Campaign record I have in my Salesforce database.  If there is no match, then the code finishes.

This is useful for me because I get Leads sent to me everytime someone Test Drives, Installs or clicks Contact Me on an Arrowpointe AppExchange product.  I will typically get duplicates in my org, which I go about merging.  If I don’t have Campaign Member records, I lose sight of all the activity someone did on AppExchange across my apps!  With Campaign Members populated, I can have a record of someone test driving Auto vCard and then installing it, then test driving User Adoption Dashboard and then installing it and then test driving Arrowpointe Maps and so on. Also, using Campaign Members, I am essentially compiling an easily accessible list of people who have performed certain actions on my AppExchange apps.

Up until now, I was manually creating Campaign Members and then merging my Leads.  However, now I let Salesforce take care of the tedious Campaign Member creation (I still do the merging).

The code is simple and is officially living on the Arrowpointe Google Code site.  The code isn’t likely to change anytime soon, so I am posting a working copy here for you. This is a useful script to have in your quiver in case you need it. The nice thing is that the trigger only takes effect when there is a match. Thus, in order to get it working for a specific Lead Source, all you need to do is create a Campaign with a matching name. Voila!

Trigger

trigger Create_CampaignMember_For_New_Leads on Lead (after insert) {

	try {	
		
		if (Trigger.new.size() == 1) {
			
			List <CampaignMember> cm = new list<CampaignMember>();
			
			for(Lead L : Trigger.new) {
				
					String cname = L.leadsource;
					
					// Added for AppExchange Partners that get leads via AppExchange where Salesforce added the "dup-" term to signify a duplicate
					String replaceText2 = 'dup-';
					cname = cname.replace(replaceText2,'');
					
					List <Campaign> c = [select id, name from Campaign where name = :cname limit 1];
					
					if(!c.isEmpty()){
						CampaignMember cml = new CampaignMember();
						cml.campaignid = c[0].id;
						cml.leadid = l.id;
						cm.add(cml);
					}
			}
			
			if(!cm.isEmpty()){
				insert cm;
			}
		}
		
		
	} catch(Exception e) {
		system.debug ('error: ' + e.getMessage() );
	} 
}

Test Class

public class Create_CampaignMember_For_New_Leads {

	static testMethod void Create_CampaignMember_For_New_Leads() {
	
			List <Lead> Leads;
			   
			// Create a Lead with a Lead Source matching a Campaign
			Lead L1 = new Lead();
			L1.lastname = 'Create_CampaignMember_For_New_Leads';
			L1.firstname = 'Test For';
			L1.company = 'Company Name';
			L1.leadsource = 'Web'; // Make sure you have an active Campaign with this name
			   
			insert L1;
			String holder = L1.id;
			
			List <CampaignMember> cm = [select id from CampaignMember where leadid = :holder limit 1];
			system.AssertEquals(1,cm.size());
			
			// Create a Lead without a Lead Source matching a Campaign
			Lead L2 = new Lead();
			L2.lastname = 'Create_CampaignMember_For_New_Leads';
			L2.firstname = 'Test For';
			L2.company = 'Company Name';
			L2.leadsource = 'No Matching Campaign'; // Make sure you DON'T have an active Campaign with this name 
			   
			insert L2;
			
			String holder2 = L2.id;
			
			List <CampaignMember> cm2 = [select id from CampaignMember where leadid = :holder2 limit 1];
			system.AssertEquals(0,cm2.size());
	                       
	}
}

Let me know what you think, if I am doing anything inefficiently and other suggestions you might have. This is my first foray into Apex Code, so I had to start simple.

Comments (6) comments feed

Do you backup your Salesforce data?

No matter where you store your data, it is always good practice to maintain backups. After all, it is your data and you need to be responsible for it, although we trust 3rd parties to keep it safe and backed up for us too.

When it comes to Salesforce, are you maintaining backups? If so, how? Please comment and enlighten the community on some good options you’ve deployed. Some that spring to mind for me are:

  • Weekly Data Export – Inside Salesforce, Enterprise and Unlimited Edition customers (for a fee, Professional Edition can do this) can request a weekly export of their data. The result is a ZIP of CSV files containing the raw data for each Salesforce object.
  • Custom Script – Write custom code to access Salesforce via the API and grab all the data into your own database. Mike Simonds has some scripts to help get you started if you use PHP.
  • AppExchange Application – Use an AppExchange application to do the dirty work to automate this process. Visit the AppExchange to read about the various solutions.
    • Sesame Software – I find this tool very easy to use and can get your Salesforce data into Oracle, MySQL, SQL Server, etc. in a hurry.
    • CRM Fusion – They have a feature to backup your data directly into MS Access.
    • Other Data Loading/Moving Tool – Pervasive, Informatica, Bluewolf, Apatar and others have solutions to move data from place to place with a connector to Salesforce built in.
  • Do Nothing – Let Salesforce take care of it. They have a proven track record of keeping your data from disaster.
  • Something else?

I wish I personally had a better answer, but I am currently using the “Do Nothing” approach and occasionally make backups to MS Access using Demand Tools “just in case”.

How about you? Experiences, recommendations and general thoughts are welcome in the comments.

Comments (27) comments feed

Derive a Rating using Formulas

On a recent project, I was helping my client prioritize their lead data. They were having trouble with the default Rating field in Salesforce because 1) it required a user to populate the record and 2) the rating should change with the passing of time.

I came up with a formula that takes the Lead Status and Expected Close Date (a custom field) into account in order to derive a Rating value on a Lead. This client happens to use Leads for their entire sales cycle and converts Leads once they are sold, but this concept would apply to equally to Opportunities as well.

Requirements

The simplified requirements are to derive a Rating value as follows:

Lead Status / Days to Close 0 to 30 days 31 to 60 days 61 to 90 days over 90 days
New New New New New
Contacted Warm Warm Cold Cold
Interested Warm Warm Warm Cold
Presentation Hot Warm Warm Cold
Sent I/O Hot Hot Hot Warm
Sold Won Won Won Won
Lost – No Contact Lost Lost Lost Lost
Lost – No Budget Lost Lost Lost Lost
Lost – Not Interested Lost Lost Lost Lost

They plan to manage their pipeline closely using the derived Rating field. They want the Rating to get "better" as time passes, so that they can reinforce the use of the Expected Close Date field. For example, if there is a Lead in Presentation status and it gets forgotten by Sales, that lead’s rating will eventually get to "Hot" as the Expected Close Date approaches or is passed. Management will be asking about that Lead and it will either be accurate or the Expected Close Date will be updated to better reflect reality. Over time, they will really be able to trust the Expected Close Date field, which is a new field for them.

Solution

To accomplish this, I did the following:

  1. Added a custom field on Leads called Expected Close Date. This would be filled out by the end-user and acts like the Close Date does on an Opportunity.
  2. Added a custom formula field called Days to Close that calculates how many days away the Expected Close Date is. The formula is as follows:

    Expected_Close_Date__c – TODAY()

    This field was added to the Page Layout because it is pretty useful in and of itself. Also, the client is on Professional Edition, so we wanted this field available in reports and that’s how you make it visible.

  3. Added a custom formula field called Rating (derived) that calculates a rating value using the requirements from the table above. I use this formula approach (using the CASE function) whenever I am doing some kind of matrixed formula result.
    CASE(1,

    IF(ISPICKVAL( Status ,"New"), 1, 0),"New",

    IF(AND(ISPICKVAL( Status ,"Contacted"), Days_to_Close__c <= 60), 1, 0),"Warm",
    IF(AND(ISPICKVAL( Status ,"Contacted"), Days_to_Close__c > 60), 1, 0),"Cold",

    IF(AND(ISPICKVAL( Status ,"Interested"), Days_to_Close__c <= 90), 1, 0),"Warm",
    IF(AND(ISPICKVAL( Status ,"Interested"), Days_to_Close__c > 90), 1, 0),"Cold",

    IF(AND(ISPICKVAL( Status ,"Presentation"), Days_to_Close__c <= 30), 1, 0),"Hot",
    IF(AND(ISPICKVAL( Status ,"Presentation"), Days_to_Close__c <= 90), 1, 0),"Warm",
    IF(AND(ISPICKVAL( Status ,"Presentation"), Days_to_Close__c > 90), 1, 0),"Cold",

    IF(AND(ISPICKVAL( Status ,"Sent I/O"), Days_to_Close__c <= 90), 1, 0),"Hot",
    IF(AND(ISPICKVAL( Status ,"Sent I/O"), Days_to_Close__c > 90), 1, 0),"Warm",

    IF(ISPICKVAL( Status ,"Sold"), 1, 0),"Won",

    IF(ISPICKVAL( Status ,"Lost – No Contact"), 1, 0),"Lost",
    IF(ISPICKVAL( Status ,"Lost – No Budget"), 1, 0),"Lost",
    IF(ISPICKVAL( Status ,"Lost – Not Interested"), 1, 0),"Lost",
    "")

  4. The Lead Views and Reports we made for reps are driven by this derived field. By doing this, they have 1 simple value to prioritize on. Also, as new Lead Statuses emerge or if we re-define what "Hot" means, we only need to modify the formula. When we do, all the reports and views automatically pick it up since they are not hardcoding filters on the Lead Status of Expected Close Date fields.

Does anyone else use an approach like this? Any suggestions to make it better?

Depending upon complexity, it is possible to hit formula size limits. Especially considering the need to use the ISPICKVAL function for the Lead Status. Vote on this idea to fix the ISPICKVAL issue.

Comments (2) comments feed

Next entries » · « Previous entries