Tuesday, 31 March 2015

Cleared my ADM 211 Salesforce Certified Advanced Administrator (SP15) exam!

ADM 211 Salesforce Certified Advanced Administrator (SP15)

Tackling ADM 211

I recently completed my Salesforce.com ADM 211 Advanced Administrator exam. I used a lot of great material available on various blogs/websites for revision, and I just wanted to share those resources here for anyone else working towards this.

Good luck in your own exam.

Wednesday, 25 March 2015

Count Activities against an Opportunity

Counting Activities logged against an Opportunity

Whenever I get a request for a new Trigger I usually hit Google first to see whether some developer has kindly shared a similar challenge and code solution.

On this occasion I was asked to display a count of all activities logged against an Opportunity record. This should answer the question of "how much work did we do for this?".

I found this first example of a class which would perform roll up summary type triggers for Open Activities across Account, Lead, Contact, and Opportunity and started looking at the structure.


Thursday, 19 March 2015

Extracting Values from a Delimited File Part Deux

Extracting Values from a Delimited File Part Deux

Since working on this Trigger last I've been reading a lot about Trigger best practice.

I particularly like the requirement of having a single Trigger per Object in Production environments and will move to this model over time.

With that set, here is v2 of this trigger.

trigger TaggedState on NVMStatsSF__NVM_Agent_Summary__c (before insert, before update) {
    NVMStatsSF__NVM_Agent_Summary__c[] agentSummaries = Trigger.new;

and the class that now contains all of the logic:

Extracting values from a pipe delimited text file

A Trigger to extract key value pairs from a pipe delimited text file

This Trigger is incomplete. I just wanted to keep it for nostalgic purposes as it's the most complex/annoying one I've worked on so far.

I had a requirement to break down a field called Key Event String into a list of values. Then, to loop through the values searching for specific terms and adding up the totals for each.

It all sounded so simple but this particular trigger kept bringing up totals from one value and listing them against another. So Custom State 1 would actually be the total of Custom State 2.

I spent hours debugging but eventually decided to rewrite it. I'm pretty sure the answer lay where I was setting the value variable but rewriting gave me an opportunity to tidy it up and move the handling into a class.

Monday, 16 March 2015

Checking for Working Days and Company Holidays

Checking for Working Days and Company Holidays

In this post I talk through a Trigger I wrote to check whether a Custom Activity record was created on a weekday, or during a public holiday.

The Problem

My customer was storing their work activity in a Custom Object called Project Time Log.

At the end of the month they would run a report showing how much activity had taken place.

The problem was that Salesforce.com doesn't seem to have any way out of the box of excluding Weekends and Public Holidays out of report criteria so the customer's reports were alwaysinaccurate.
Therefore I had to develop a Trigger to check each submitted activity log and decide whether the activity was on a working day/or on a scheduled public holiday.


Saturday, 14 March 2015

Conditional count of Related List

Counting items in related list

Following on from my previous blog where I mentioned that some things in Salesforce.com look complicated at first but actually have a really easy solution this requirement sounded easy but required a trigger in the end to solve it.

The problem

My customer wanted to count have many Activity Records with a given criteria were linked to a Case record up to the point where the case was closed.

This count would be populated on a number field on the Case Object called: Number_of_Calls__c

This field would show how many interactions took place from Case Open to Case Close.

Unfortuantely, buried within the activity history on the case there were other non-relevant activities so a way to count specific tasks was required.

At this point, if Activity History was a Master Detail relationship under the Case then I could have added a Roll Up summary, but as it's a Lookup there's currently no easy way to do this out of the Salesforce.com box.

Your 2 options are to either install a 3rd party app like Rollup Helper or Code a custom trigger.

For complete flexibility I went down the trigger route - here's the code.

Friday, 13 March 2015

analytics:reportChart Embedding Salesforce Charts in Visualforce

Embedding Salesforce Charts in Visualforce

So many updates get added to Salesforce.com all of the time that I'll quite often find a new feature that passed me by.

The problem

I was asked to make some improvements to a set of Reports & Dashboards on a customer ORG. Based on the requirements, I knew that I would  have to create Visualforce pages to give me full flexibility over the layout, but I wasn't looking forward to recreating all of the reports.

Today's bonus feature is the Visualforce analytics:reportChart tag that lets you grab the URL or any report that has a chart/graphic in it and then drop that chart into any Visualforce page.

Step 1

Build a normal Salesforce.com report and add a graph to it

Step 2

Grab the ID of the report you just made from the URL

Step 3

Go to your Visualforce page and add the following code

                <analytics:reportChart reportId="URL OF YOUR REPORT"/>

Step 4

Preview your Visualforce page - you should see the same report Chart appearing in the content. As an additional bonus, the Analytics tag includes a timestamp of the last refresh and is clickable through back to the source report.

Any error messages?

I experienced one error message when setting this up for the first time. As the Visualforce page could potentially be accessed by anyone you need to ensure that the source report is also publically accessible.

Friday, 6 March 2015

Trigger that posts to Chatter automatically

The problem

Posting to Chatter via Apex Trigger

This requirement comes up fairly regularly now - saving users time by automatically posting to Chatter if an update happens within Salesforce that needs calling out.

Examples could be - An urgent case has come in - a customer account has gone Red - there is a service issue etc.

This particular chatter trigger looks for any cases marked with a skill of "Escalation" and automatically posts it out on Chatter with a Topic tag marked "Escalation".

To implement this trigger in your ORG you would need to add a field called "Skills__c" on your Case object and populate it with "Escalation" when you insert a new case.

trigger EscalationPost on Case (after insert) {
//Only firing trigger on initial case creation - amend if requirement to before update etc
    for(Case myCase: Trigger.new){
//Change criteria of trigger if required
            FeedItem post = new FeedItem();
            post.ParentId = myCase.id;
//Change body of Chatter post in between single speech marks below
            post.Body = 'Escalated Complaint Case! Retention team need to engage. [#Escalation]';
            insert post; 
        } //end if
    } //end for loop
} // end trigger

Thursday, 5 March 2015

Change of record owner after Approval Process

The problem

Change of record owner after Approval Process

I wanted to write a 2 step approval process.

One where you would send a record through for transfer, but would have to be accepted by the new person.

Salesforce is very good at 1 way approval process. (I.e., I send the record to you, but then you have send it somewhere else if you don't want it). However, in this instance it was important that the record was accepted before transfer.

This example needs an approval process set up where approval updates ChangeManagerApproved to true. We also need 2 owner fields - Escalation_Manager__c and New_Escalation_Manager__c.
// Comment
* Reassigns Escalation Manager on Escalation after approval process is complete
* Output of approval process is ChangeManagerApproved = True
* If ChangeManagerApproved = True and yet the Escalation Manager and 
* New Escalation Manager fields then trigger should fire
* Only to fire on Update
trigger reassignEscalationManager on Escalation__c (before update) {
    try {
        Set<Id> accountIds = new Set<Id>();
        Map<Id, Id> accountOwnerIdMap = new Map<Id, Id>();
        // all the accounts whose owner ids to look up
        for ( Escalation__c c : Trigger.new ) {
            if (c.ChangeManagerApproved__c == True && (c.Escalation_Manager__c <> c.New_Escalation_Manager__c)) {
                System.debug('Change of manager approved and new/old manager different - trigger firing');
                //Final action needs to be changing ChangeManagerApproved__c to False to prevent re-fire
                c.Escalation_Manager__c = c.New_Escalation_Manager__c;
                c.ChangeManagerApproved__c = False;
                c.New_Escalation_Manager__c = Null;
            } //end if
        } //end for        
    } //end try
    catch(Exception e) 
    { //catch errors
        System.Debug('reassignEscalations failure: '+e.getMessage()); 
        //write error to the debug log
} //end trigger

Reassigning Contact Owner in Salesforce to Account Owner

Salesforce Snippets

On this blog I'll share any code snippets that I develop in Salesforce.com. It'll be a good reminder for me, and I hope it helps other people out there who might come across similar challenges.

Getting started

Right, first challenge of the day - setting up Blogger.com to handle code snippets and posting a test trigger.

That turns out to be quite straightforward thanks to a great post from Geektalkin - thanks!


Right, now that I can copy in Trigger code and maintain it's formatting here's the first example:

What does this trigger do?

This trigger changes the ownership of any Contact record in Salesforce that is added or updated. The trigger ensures that all of the Contacts relating to an account are owned by the Account Owner.

Original source:

This is a slightly modified version of an example trigger posted on the Salesforce.com success community.

//Reassigns Contact to Account Owner

trigger reassignContact on Contact (before insert, before update) {
   try {

        Set<Id> accountIds = new Set<Id>();
        Map<Id, Id> accountOwnerIdMap = new Map<Id, Id>();
        // all the accounts whose owner ids to look up
        for ( Contact c : Trigger.new ) {
            if(c.accountId <> null){
             accountIds.add( c.accountId );
        // look up each account owner id
        for ( Account acct : [ SELECT id, ownerId FROM account WHERE id IN :accountIds ] ) {
            accountOwnerIdMap.put( acct.id, acct.ownerId );
        // change contact owner to its account owner
        for ( Contact c : Trigger.new ) {
            if(c.AccountId <> null){
             c.ownerId = accountOwnerIdMap.get( c.accountId );
    } catch(Exception e) { //catch errors
        System.Debug('reassignContacts failure: '+e.getMessage()); //write error to the debug log