I Found a Bug – And Salesforce Support Was Great
A week ago, I installed Scott Hemmeter’s amazing app, Geopointe. Everything was going smoothly until I decided to manage the licenses I had assigned to the app.
I successfully managed the licenses and tried to click the “Back to Previous Page” link, but something broke:
Here’s a video of the problem in action: Managing Licenses Previous Page Link Is Broken.
This is a story about the super-responsiveness of the salesforce.com support team. (I’m not including peoples’ names, but for any employees, it is Case 03580410.
I submitted my case May 24, and the first response was received May 25. The first person, JW, sent an email asking me to clear my cache and change my browser. I cleared the cache, used multiple browsers, and even tried with different packages in my org. All had the same problem.
May 27, JW tried to call me, but I was unavailable. (Good follow-up.) He also sent an email, and I promised to get back to him. I replied the next day and told him that the problem occurred even on different computers, with different browsers, and different packages. He quickly replied and asked to login to the org to try to recreate the issue.
The next step was for us both to login to the org while on the phone. He verified that the problem was happening, and we noticed something interesting:
Clicking “View Installed Packages” gives a URL like …/0A3?setupid… and clicking Manage Licenses gives a long URL including ?allPackageId=033600000004Pjn. The broken link was https://na7.salesforce.com/033600000004Pjn. See anything there?
The “Back to Previous Page” link should have taken me back to the /0A3 page (exactly as the browser Back button did), but instead tried to show me the inner workings of a package, which Salesforce clearly cannot display.
JW suggested that this was a problem with the app, as the link was on a page managing licenses for two managed apps by the same publisher. I quickly explained that I had developed managed applications before and that I knew that this was a feature created by salesforce.com, and therefore salesforce.com was responsible for the broken link. He said that he understood and that he would escalate the case to Tier 2.
Quick recap so far: Salesforce Support has responded quickly, so they get good points for that, but I’ve not been available to talk. The support rep has tried everything he and I can think of, and we have found a problem in a link that directs to the wrong URL. I’m up to Tier 2.
At this point, I don’t have any more communication until June 8. I’m not fussed, as I know it’s a real issue, but I have a good workaround (use the Back button).
June 8, GE sends me an email saying that he reproduced the error and escalated it to Tier 3 for further investigation. I thanked him and said that it felt bittersweet because I had found a real problem and it’s no fun when my beloved Salesforce CRM has a bug. He validated my feelings. (Warm fuzzies all around.)
The next day, GE sends an email. This is the text:
An issue tracking number has been attached to your case, and is awaiting prioritization by R&D. I will receive updates on this, but the timeline for rollout may not be known for some time. Just wanted to let you know that it has been queued up for a fix, and I will let you know more when I know. Feel free to check in any time, and also bear in mind that there is a very good workaround for this, being the browser back button J We’re just required to mention workarounds, even though I know you know this. Let me know if you need anything else.
Music to my ears! An issue tracking number! R&D looking into it! I had found a real bug and it would be fixed!
The point of all this is to say that for all the complaints (many justified) that people have about salesforce.com support (why do they automatically want login access when the problem sometimes has nothing to do with anything in an org???) there are times that they really shine.
Thank you, JW and GE. At least I know a fix is coming.
Sophisticated DateTime “Formula Fields” with Apex and Field-Level Security
What do you do when you want to calculate a formula-like field but a regular formula won't work?
Salesforce CRM's formulas handle dates very well. If you want to enter a date value and have formula fields display, for instance, mydate__c + 21 days, that's simple. Just use mydate__c + 21.
Side note: If you try going the long way around and use DATE( YEAR( mydate__c ), MONTH( mydate__c ), DAY( mydate__c ) + 21 ) and mydate__c = 09/17/2010, Salesforce returns #Error! because there's no date 09/38/2010. Similarly, adding three months to a date like 1/31/2010 will also give an error. More about this in a future post.
DateTime fields are like Date fields, but they include... wait for it... a time component (and can be created in the running user's local time zone or in GMT).
Here's a use-case for a DateTime formula field:
A photography studio schedules photo shoots, and different packages include different durations. Similarly, we could use a hair salon which offers different services, each with a different duration, a dentist... you get the idea.
Requirements:
- Enter a DateTime for an appointment start time (
starttime__c) - Enter a duration (though in a production system, I'd include a value on the
Product2sObject, we'll just enter a value here) (minutes__c) - Display a read-only DateTime field with the end time (
endtime__c) - The end time must be read-only to all users, like any formula field
Here's what won't work:
- A formula field won't work because there are no MINUTE(), HOUR(), SECOND() formula functions
- Workflow won't work because it depends on formulas to fill new values for date/datetime fields
That leaves Apex. First, the configuration:
- Create DateTime field
starttime__c - Create DateTime field
endtime__c - Set
endtime__cfield-level security to Read-Only for all profiles - Create Number (18,0) field
minutes__c - Create a trigger on the sobject
Here's the trigger:
trigger timeTrigger on TestObject__c (before insert, before update) {
for (TestObject__c t : Trigger.New){
if(t.StartTime__c != null && t.minutes__c != null){
datetime myDateT = t.StartTime__c;
double d = t.minutes__c;
Integer shootmins = d.intValue();
if(mydateT != null && shootmins != null)
t.EndTime__c = myDateT.addminutes(integer.valueof(shootmins));
}
}
}
Regular readers will note that I do usually split triggers into a trigger and a class, but I've not done so here purely for the sake of brevity.
Here's the test code:
public without sharing class shootTimesTriggerTest {
private static testMethod void ShootCalculateEndTime_PositiveTestCases() {
TestObject__c to;
TestObject__c l;
test.starttest();
l = new TestObject__c (name = 'test');
datetime myDateTime = datetime.newInstance(2008, 12, 1, 12, 30, 2);
l.StartTime__c = myDateTime;
l.minutes__c = 90;
insert l;
to = [SELECT id, EndTime__c FROM TestObject__c WHERE id = :l.id];
datetime newDateTime = datetime.newInstance(2008, 12, 1, 14, 0, 2);
system.assertequals(to.EndTime__c, newDateTime);
l.minutes__c = 45;
update l;
to = [SELECT id, EndTime__c FROM TestObject__c WHERE id = :l.id];
newDateTime = datetime.newInstance(2008, 12, 1, 13, 15, 2);
system.assertequals(to.EndTime__c, newDateTime);
test.stoptest();
}
private static testMethod void OppCalculateEndTime_NegativeTestCases() {
test.starttest();
TestObject__c l = new TestObject__c (name = 'test');
l.minutes__c = null;
insert l;
system.assertequals(l.EndTime__c, null);
test.stoptest();
}
}
A few points about how this works:
- Triggers run in System mode, so they don't respect field-level security. Thus, we can set a field to read-only for all profiles, and the EndTime__c field will still be updated.
- The test code runs in System mode as well, avoiding any potential problems if the field were set to invisible to a profile and we used System.RunAs() to test for various profiles.
- Although I'm not a fan of using SOQL queries this often, I used these in the interest of saving time. Keep in mind that if you had quite a few queries in your regular code, adding these two might put you over the limit, so use queries sparingly!
- This is the only way I know of to add minutes to a DateTime.
Did I miss anything? Please let me know in the comments.
New Opportunity Page Layout – With Highlights Panel!
Yesterday, I enabled the new Opportunity page layout in my Developer Spring ’10 Preview org, and it took a few steps, so I thought I’d share them with you.
Firstly, you’ll need to contact salesforce.com to get this feature enabled.
Then be patient. It takes a minute or two for the update to propagate. Clearly, something was churning in the Force.com platform background!
Now we’ll navigate NOT to the Setup | Customize | User Interface screen (where this should be enabled). Instead, we’ll go to the Opportunity Page Layout screen.
Follow the cool prompts. They make it so easy, a … well, you know what I mean.
Note: You can only show fields in the Highlights Panel if they are in the page layout. (I have a feeling this has to do with Professional Edition or printable layouts, but I’m just guessing.)
Once you’ve done this for each page layout, click on the big button.
At this point, each user can enable the bar. I have no idea why the admin can’t just force this on all users – or maybe I missed something – but it seems to be an opt-in feature.
Here’s the link to enable the feature. Of course, you may wish to watch a video as well!
And here it is!
It’s interesting that if you have this enabled, certain user interface settings (yes, at Setup | Customize | User Interface) cannot be changed:
Here’s my prediction: We will start to see two major mistakes during Salesforce demos:
- We will continue to see the link asking if we want more information on inline editing (after more than a year, it’s time to turn that off, people).
- At the top of the Opportunity detail page, we will see this link.
And I will continue to think less of all demonstrators who make these mistakes.
Happy Spring 2010!
Get Documents and Attachments out of Salesforce
As Content will be included in all Salesforce licenses (for completeness, I'll add 'to some degree') with the Spring '10 release, orgs will be faced with the daunting prospect of getting their documents and attachments out of Salesforce and into Content.
I had this problem when Content was first released and I was asked to be one of the first SysAds to use it. At the time, we used Solution 1 (below), but since then, other products have been released to help with this.
Why is it even an issue?
- Surely we can download each file? Yes, but who wants to?
- Can't we do a Data Export and then upload those to Content? Yes, but all the files are renamed with their 15-character Ids, making renaming them all-but-impossible.
salesforce.com and DreamFactory to the rescue!
Solution 1
Summary: Use a script to rename all exported files. A (wonderful!) salesforce.com employee, Nick Marcantonio, wrote a Perl script to perform the transformation. Here it is, in all its glory:
# Nick Marcantonio
# nmarcantonio at salesforce.com
# 08/07
$file = 'Attachment.csv';
open (F, $file) || die ("Could not open $file!");
$line = <F>; #read first line which is nothing but column headers
while ($line = <F>)
{
($id,$name) = split ',', $line;
chomp($id);
$id =~ s/\"//g;
chomp($name);
$name =~ s/\"//g;
#print "$id : $name\n";
$result = rename($id, $name);
#print "$result\n";
}
close (F);
The instructions:
If you've done a data export you've noticed that all attachments are placed in the Attachments subfolder and named with their salesforce ID, not the actual file name or extension. One must then consult the Attachment.csv file included in the data export to find the name associated with the ID and rename the file. Attached to this solution is a Perl script that will rename all of the exported attachments to their proper names. Please follow these steps to run this: 1. Perform a data export and unzip the resulting zip file 2. Launch the data loader and export from the Attachments table ONLY the Id and Name column. This file must be named Attachment.csv. 3. Install ActivePerl. This will allow perl scripts to be run on a Windows machine. ActivePerl is available here (http://www.activestate.com/activeperl). 4. Copy the Attachment.csv file and the attached AttachmentParser.pl file to the Attachments subdirectory of the data export. 5. Double-click on AttachmentParser.pl. All of the files named with their salesforce IDs will be renamed with their proper names and file extensions. (This solution will work for documents as well. Follow the same procedure and be sure to name the extract from the Documents table Attachment.csv)
Note: This will not preserve folders, as far as I know. You may be able to recreate this by exporting the Folder table and doing some work on that, as the Document table does include a FolderId column.
A heartfelt thank-you to Nick Marcantonio for his help!
Solution 2
Install DreamFactory's FREE DreamTeam Document Management application from the AppExchange to drag-and-drop your Documents to your desktop.
This doesn't work with Attachments, though, so you may need to use another method for them.
Please let us know how it goes - good luck and enjoy Content!
Filtered Lookups, Validation Rules, and Order of Execution
Reading the cheatsheet for Filtered Lookup (beta), I noticed an interesting line:
Lookup filters function similarly to validation rules when you save a record. That is, actions that cause related records to save, such as changes to a roll-up summary fields, also trigger the lookup filters on the related record and block the save.
The implications for this are massive. Let's explore two examples:
Example 1: Filter as Validation Rule from Parent Record
- We create a lookup on a Child object to Parent.
- We filter the lookup to EXCLUDE Parent.Status = 'Closed' (Parent.Status is only Open or Closed.)
- We can edit the Child records as long as the Parent Status is not Closed.
- When Parent.Status is changed to Closed, existing related Child records are not affected...
- BUT if we attempt to edit a Child when the Parent is Closed, Force.com will throw an error (which we can customize) beause that the Lookup is invalid.
- (and clearly we cannot add new Child records either)
Conclusion: Thus, Filtered Lookups act much like Validation Rules. A quick experiment shows that Filtered Lookup errors actually fire before Validation Rules.
Example 2: Filter as Validation Rule on Roll-Up Summary (from Child Record) - what the line above was referencing
- Use the above example, but change the lookup to a master-detail relationship
- Create a Roll-Up Summary field to count all child records
- Prevent saving more than 10 child records for one parent record
Here, we have triggered a filter error without touching a parent record, yet we throw an error based on a value on the parent record.
This second example is significant because we could already prevent more than 10 child records from saving, but doing so required a Roll-Up Summary field on the parent object AND a Validation Rule on the child object. Now we can replace the Validation Rule with the Lookup Filter, though we still need the Roll-Up Summary field. Whether or not this simplifies things is definitely up for debate...
Conclusion
This is a very powerful feature! Thanks to salesforce.com for rolling it out, even in beta form.
Real world example: The above example would be great for Time Sheet Entry and Time Sheet Header objects, as they would create, in effect, a validation rule on the Header record preventing editing of any child records. Awesome!
For further reading, check Salesforce Help's Lookup Filters examples.













