Wednesday, February 6, 2013

Salesforce.com Professional Edition Pain Points


The most common editions of Salesforce.com that businesses will sign up for are Professional Edition and Enterprise Edition. A quick way to tell what version you are on is when using google chrome as your browser if you go to the home tab in Salesforce, the chrome tab will say "Salesforce.com - Professional Edition" or "Salesforce.com - Enterprise Edition".

There are a number of items that are sorely missed when going from Enterprise to Professsional Edition, below is a list of items missing from Professional Edition that can cause some headaches.

  • Workflow Rules
  • Apex Triggers
  • Apex Classes
  • API
  • Joined Reports
  • Custom Profiles
  • Record Types
  • Grant Login Access


You can see a more detailed list of what is offered in all of Salesforce's editions here:
http://www2.sfdcstatic.com/assets/pdf/datasheets/DS_SalesCloud_EdCompare.pdf

Monday, February 4, 2013

Pull Record Type ID in Apex


This is just a quick query that grabs the record type id and assigns it to a string. I am always needing this so decided to put it up on the blog for reference. You need something like this when specifying a record type because if you would hard code the id into the trigger/class in production it would fail because everything has a different id from Sandbox to Production.

RecordType r = [select id from RecordType where name = ‘Account Recortype’ AND SobjectType = 'Account'];
String AcctRecordtypeID= r.id;

So the string AcctRecordtypeID will now have the record id of the record type pacesetter.

If there are multiple record types out there with the same name associated to different object, you can just adjust the where statement to be more specific about where to pull the RecordType from.

Alternatively the same query could be used with a list. This will prevent an error in the event that there is no recordtype with the name specified.

List<RecordType> recType = [select id from RecordType where name = 'Pharma' AND sobjecttype = 'Account' AND IsActive = TRUE limit 1];

if(!recType.isempty()){
String PharmaRecordTypeID = rectype[0].id;

}

Friday, February 1, 2013

Map Described

I have recently begun to understand the value of Maps when writing Apex triggers and classes, they can be very powerful. The most common Map used is the 'Map <ID, sObject>' this means that the Key of the Map is the ID and the value is the object related to the ID.

Typically you will see it initiated in this way:

Map<ID,sObject> sObMap = new Map<ID,sObject>([SELECT *fields* FROM sObject WHERE *conditions*]);

This is how I used it in a recent bit of code I wrote. This trigger copies the owner of the Account related to a case down to the case record on a custom field.




trigger CaseBeforeInsertTrigger on Case (before insert) {

    Set<Id> accountIds = new Set<Id>(); //set to hold list of Accounts

    for (Case c : Trigger.new) { //loop through all cases and pull the Account ID into the set above
        if(c.AccountId != null) {
        accountIds.add(c.AccountId);
        }      
    }
 
    Map<ID, Account> acct = new Map<ID, Account>([select id, ownerid from Account where id = : AccountIds]); //create a map where the ID is the Key and the Value is the sObject (Account)

    if(acct.size() > 0) {
        for(Case c: Trigger.new) { //if there are values in the map, loop through the new cases
      if (c.AccountId != null){
     
          Account acctUpdate= acct.get(c.AccountID); //look back into the map and using the Account ID grab the Account sObject
          c.Account_Owner__c = acctUpdate.OwnerId;
     
      }
        }
    }
}

Thursday, January 24, 2013

Adding an Activity to a Chatter Feed Using a Trigger

I have had many requests for a trigger like this over time and Kevin Swiggum had a post back in 2010 with a simple to implement solution for this request, the link is below.

http://www.radialweb.com/2010/09/adding-activity-to-a-chatter-feed-using-triggers/


Below is a copy of the code Kevin posted:

trigger ChatterActivity on Task (after insert, after update) {

    List<FeedItem> feedItems = new List<FeedItem>();

    //We want to show the User name as assignedTo. The only way to get to that is by querying the user table.
    Set<ID> ownerIds = new Set<ID>();
    for (Task t : Trigger.new) {
        ownerIds.add(t.ownerId);
    }
    Map<ID,User> userMap = new Map<ID,User>([SELECT ID, Name FROM User WHERE ID IN :ownerIds]); //This is our user map

    //Now loop though the new/updated tasks and create the feed posts
    for (Task t : Trigger.new) {
        if (t.WhatId != null) {
            FeedItem fitem = new FeedItem();
            fitem.type = 'LinkPost';
            fitem.ParentId = t.WhatId;
            fitem.LinkUrl = '/' + t.id; //This is the url to take the user to the activity
            fitem.Title = 'View';  //This is the title that displays for the LinkUrl

            //Get the user by checking the userMap we created earlier
            User assignedTo = userMap.get(t.ownerId);

            fitem.Body = ((Trigger.isInsert) ? 'New' : 'Updated') + ' Activity ' + ((t.ActivityDate != null) ? t.ActivityDate.format() :'')
                        + '\nAssigned To: ' + ((assignedTo != null) ? assignedTo.name : 'Unknown')
                        + '\nSubject: ' + t.Subject
                        + '\nStatus: ' + t.Status;

            feedItems.add(fitem);
        }
    }

    //Save the FeedItems all at once.
    if (feedItems.size() > 0) {
        Database.insert(feedItems,false); //notice the false value. This will allow some to fail if Chatter isn't available on that object
    }
}


And here is the test class associated

public with sharing class ChatterUnitTests {

    /*
    *    Test the ChatterActivity trigger which inserts chatter feed posts whenever a task is inserted on the parent object
    */
    public static testMethod void testChatterActivity() {
        //create the test account
        Account a = new Account(name='Test Account');
        insert a;

        //create a task on that account
        Task t = new Task(whatId=a.id);
        t.Subject = 'This is a test activity for chatter';
        t.ActivityDate = System.today();
        t.Status = 'In Progress';
        t.Description = 'Hello, this will be chattered';

        insert t;

        //Make sure Account has the feed enabled. If it does, make sure the chatter feed post is there
        Schema.DescribeSObjectResult r = Account.SObjectType.getDescribe();
        if (r.isFeedEnabled()) {
            List<AccountFeed> posts = [SELECT Id, Type FROM AccountFeed WHERE ParentId = :a.id];
            System.assertEquals(1, posts.size());
        }
    }
}







Wednesday, January 16, 2013

Bulkify Apex Triggers

This is something that has been harped on in every Apex class I have taken and it finally clicked with me! This is probably much more clearly explained in the link below from developer.force.com but I want to document it here for my own knowledge.

http://wiki.developerforce.com/page/Best_Practice:_Bulkify_Your_Code

  • Create a SET before entering trigger.new that will hold the ID of all SObjects being updated or looped through
  • Pull a list of the IDs of the SObjects that need to be updated when looping through trigger.new and add them to the SET defined above
  • If that SET contains items then create a list of SObjects and use 1 SOQL query to populate the entire list
  • Now you can loop through the entire list of SObjects and make updates from there...

Below is an example of this in action:

if(trigger.isBefore){
//Set to hold the list of Contract Id's that will be queried for the associated Account 
Set<String> ContractIds = new Set<String>();
//loop through all Agency Commission Records and add the Id of the Contract to the ContractIds Set
for(Agency_Commission__c ac : trigger.new){
if(ac.Contract__c != null){
ContractIds.add(ac.Contract__c);
}
}
if(!ContractIds.isEmpty()){
//Use a single query to pull a list of Contract SObjects
List<Asset_C__c> contractList = [select id, Account__c from Asset_C__c where id =: ContractIds];

//Map to hold the Contract ID as the Primary Key and the Account ID as the Key Value
Map<string, string> ContractAndAccount = new Map<string, string>();
//Loop through the List of Contracts and add the Contract ID and Account ID to the map
for(Asset_C__c c1 : contractList){
if(c1.Account__c != null){
ContractAndAccount.put(c1.id, c1.Account__c);
}
}
if(!ContractAndAccount.isEmpty()){
//Loop through the original Agency commission values and assign the Account based on the ContractAndAccount Map
for(Agency_Commission__c acUpdate : trigger.new){
//retrieve the Account from the ContractAndAccount Map and assign it to a string value
String acctForContract = ContractAndAccount.get(acUpdate.Contract__c);
acUpdate.Client__c = acctForContract;
}
}
}
}

Friday, December 21, 2012

Delete Triggers and Classes from Production in Salesforce using Eclipse IDE

In order to remove a class or trigger from production using the force.com ide you can do the following:

1. Pull up the XML file associated to the class/trigger, it will look something like this:

<?xml version="1.0" encoding="UTF-8"?>
<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>21.0</apiVersion>
    <packageVersions>
        <majorNumber>1</majorNumber>
        <minorNumber>3</minorNumber>
        <namespace>households</namespace>
    </packageVersions>
    <status>Active</status>
</ApexClass>
2. Where is says <status> replace "Active" with either "Inactive" or "Deleted" then save the XML.
3. Now deploy this class/trigger to production and it will be removed!

Friday, November 2, 2012

Goldmine Query for Primary Email


SELECT contact1.ACCOUNTNO AS CustomerID, contact1.contact AS Contact, COALESCE(em.contsupref, '')+COALESCE(em.address1, '') AS Email, em.mergecodes
FROM contact1
 JOIN contsupp em on contact1.accountno=em.accountno
WHERE em.contact='E-Mail Address'
 AND em.rectype LIKE 'P'
 AND em.zip LIKE '_1%'