Content Latest Version Flag
Filed under: Configuration, Native Application, Salesforce CRM, salesforce.com, Tips and Tricks
Yesterday, I used Content Delivery to send a pdf to a client. Simple, right? Upload the Word document to Content, associate it with a record, and deliver the content. Surely if I upload a new version, the delivery will refer to the latest version, right? There’s no place to select that option, so I can assume, right?
Wrong.
This is the screen I see when I choose to deliver a given file. Note that I can choose whether to allow access to the original file or only to a pdf… but nothing about which version to deliver. (There are 4 versions right now.)
The document in question needed some changes, so I uploaded a new version and told the client that the old link would still work. Oops.
By default, Content Deliveries are set to refer to one specific version of a Content file. I can understand this, but surely I should be given the opportunity to change that?
To do this, view the Delivery record and see the following:
Click Edit and check the appropriate checkbox.
There you have it! A few extra clicks, but a Content Delivery can point to the latest version.
Do you use Content Deliveries? How do you like it?
PersonAccount Stay-In-Touch Gotcha
Filed under: Configuration, Native Application, salesforce.com, Tips and Tricks
I make no secret that I’m a fan of PersonAccounts. I think they’re very handy when working with individuals instead of companies, and I really like pairing them with the Relationship Groups app to make households.
I’ve always considered them as mostly-contacts. I put all Person fields on the Contact object, reserving very few for the Account object. But for some reason, I’ve usually used Billing Address as the primary address and Mailing as the secondary. No reason – that’s just how I’ve done it.
That all changed yesterday. I was prepping to demo a system to a company and decided to click the “Request Update” button to send a Stay-In-Touch email. This is not a customizable email (well, not much) in terms of the fields from the Contact that it displays, so it used the Mailing Address. Oops!
From now on, I am using Mailing and Other addresses for PersonAccounts. Billing, you’re reserved for BusinessAccounts.
Feel free to debate the merits and drawbacks of PersonAccounts below – I’ll respond to them in a future post.
Chatter BINGO Released Into The Wild
Filed under: Apex, salesforce.com, Visualforce, X-Squared On Demand
With Dreamforce 2010 behind us and Dreamforce 2011 fast approaching, the first ever crowdsourced conference application is publicly available!
Chatter BINGO has been released as an unmanaged package, meaning that all the source code is open and ready for customizing to your hearts’ content.
Chatter BINGO was conceived by Chris Shackelford and Brad Gross (@imperialstout) while chattering in the Dreamforce org, and they asked me to build it. Within two weeks, it had passed QA and was deployed to the org.
One of my favorite moments from Dreamforce was at the Tweetup (#df10tu – see you at #df11tu) when someone walked up to me with a printout of the PDF BINGO card she had generated, asking me to mark myself on the page. It always feels good to see people enjoying one’s work!
The listing is at http://www.x2od.com/getchatterbingo.
Enjoy!
Activity Type Field – Do Not Use
The field Event.Type (which is sort of the same as Task.Type) is a difficult field to use. Here are a few reasons:- Type cannot be the controlling field for a dependent picklist.
- On an Event list view (/007), even when specifying record type, the Type field cannot be edited.
- Salesforce requires you to select a default value for this picklist, as well as a default value for inbound emails... yet when I bcc my Email-To-Salesforce address, the resulting tasks have no Type value.
- Work in a sandbox (duh)
- Create a picklist field on Activity (ActivityType__c).
- Disable all Activity workflow rules, Chatter feeds, triggers, etc.
- Using DataLoader, copy values from Type to ActivityType__c.
- Using Field Level Security, hide Type from all profiles. It's just not necessary.
- Edit/reactivate all your workflow, feeds, triggers, etc.
- Use/adapt the code below.
- Test thoroughly and deploy to production.
trigger TaskTrigger on Task (before insert, before update) {
ActivityHelper.UpdateTaskType(Trigger.new);
}
trigger EventTrigger on Event (before insert, before update) {
ActivityHelper.UpdateEventType(Trigger.new);
}
This class isn't perfect, but it gets the job done. I should add try/catch statements in my test code (though I have heard recently that it's not a good idea to do so, as one may WANT the code to fail with an error if something doesn't go as expected).
// Written by David Schach, X-Squared On Demand
public with sharing class ActivityHelper {
public static void UpdateEventType(Event[] events){
for (Event e : events){
if(e.ActivityType__c == '' || e.ActivityType__c == null){
if(e.Subject.contains('Email'))
e.ActivityType__c = 'Email';
else if(e.Subject.contains('Call'))
e.ActivityType__c = 'Call';
else if (e.Subject.contains('Meet') || e.Subject.contains('Webinar'))
e.ActivityType__c = 'Meeting';
}
}
}
public static void UpdateTaskType(Task[] tasks){
for (Task t : tasks){
if(t.ActivityType__c == '' || t.ActivityType__c == null){
if(t.Subject.contains('Email ') || t.Subject.contains('Email:'))
t.ActivityType__c = 'Email';
else if(t.Subject.contains('Call'))
t.ActivityType__c = 'Call';
else if (t.Subject.contains('Meet') || t.Subject.contains('Webinar'))
t.ActivityType__c = 'Meeting';
}
}
}
private static Event newTestEvent(string subject){
Event e = new Event();
e.Subject = subject;
e.startdatetime = datetime.now();
e.DurationInMinutes = 60;
e.ActivityType__c = ''; // To account for default value.
return e;
}
private static Task newTestTask(string subject){
Task t = new Task();
t.Subject = subject;
t.Priority = 'Medium';
t.Status = 'New';
t.ActivityType__c = ''; // To account for default value.
return t;
}
static TestMethod void TestEvents(){
test.starttest();
List<Event> ourevents = new List<Event>();
ourevents.add(newTestEvent('e1'));
ourevents.add(newTestEvent('Email: Thank you'));
ourevents.add(newTestEvent('Call: (212) 555-1212'));
ourevents.add(newTestEvent('Webinar tomorrow'));
ourevents.add(newTestEvent('Meet Jack'));
ourevents.add(newTestEvent('Sales time'));
insert ourevents;
update ourevents;
for (Event e : [SELECT id, Type from Event WHERE id in :ourEvents]){
system.debug('EVENT TYPE: ' + e.ActivityType__c);
}
test.stoptest();
}
static TestMethod void TestTasks(){
test.starttest();
List<Task> ourTasks = new List<Task>();
ourTasks.add(newTestTask('e1'));
ourTasks.add(newTestTask('Email: Thank you'));
ourTasks.add(newTestTask('Call: (212) 555-1212'));
ourTasks.add(newTestTask('Webinar tomorrow'));
ourTasks.add(newTestTask('Meet Jack'));
ourTasks.add(newTestTask('Sales time'));
insert ourTasks;
update ourTasks;
for (Task t : [SELECT id, Type from Task WHERE id in :ourTasks]){
system.debug('TASK TYPE: ' + t.ActivityType__c);
}
test.stoptest();
}
static testMethod void IndividualActivityTest() {
Event e = newTestEvent('e1');
Task t = newTestTask('t1');
test.starttest();
insert e;
insert t;
e.subject = 'Meeting';
update e;
e.subject = 'Call: Geoff Peterson';
update e;
e.subject = 'Email: Here we go';
update e;
e.subject = 'Webinar';
update e;
t.subject = 'Meeting';
update t;
t.subject = 'Call';
update t;
t.subject = 'Email';
update t;
t.subject = 'Webinar';
update t;
test.stoptest();
}
}







