Teach Me Salesforce

A community approach to learning salesforce.com

Author Archive

Salesforce Trigger when Rollups Summaries Not Possible

with 6 comments

Master-Details relationships in Force.com  are very handy but don’t fit every scenario. For instance, it’s not possible to implement a rollup summary on formula field or text fields. Here’s a small trigger that you can use for a starter for these types of situations. The code for each class is available at GitHub for your forking pleasure.

So here’s the (not very useful) use case. Sales Order is the Master object which can have multiple Sales Order Items (detail object). The Sales Order Item has a “primary” Boolean field and a “purchased country” field. Each time Sales Order Items are inserted or updated, if the Sales Order Item is marked as “primary” then the value of “purchased country” is written into the “primary country” field on the Sales Order. I’m assuming that there can only be one Sales Order Item per Sales Order that is marked as primary. Essentially this is just a quick reference on the Sales Order to see which country is primary on any of the multiple Sales Order Items. Not very useful but illustrative.

The code is broken down into a Trigger and an Apex “handler” class that implements the actual functionality. It’s best practice to only have one trigger for each object and to avoid complex logic in triggers. To simplify testing and resuse, triggers should delegate to Apex classes which contain the actual execution logic. See Mike Leach’s excellent trigger template for more info.

SalesOrderItemTrigger (source on GitHub) – Implements trigger functionality for Sales Order Items. Delegates responsibility to SalesOrderItemTriggerHandler.

trigger SalesOrderItemTrigger on Sales_Order_Item__c (after insert, after update) {

  SalesOrderItemTriggerHandler handler = new SalesOrderItemTriggerHandler();
  if(Trigger.isInsert && Trigger.isAfter) {
  } else if(Trigger.isUpdate && Trigger.isAfter) { 
    handler.OnAfterUpdate(Trigger.old, Trigger.new, Trigger.oldMap, Trigger.newMap);


SalesOrderItemTriggerHandler (source on GitHub) – Implements the functionality for the sales order item trigger after insert and after update. Looks at each sales order item and if it is marked as primary_item__c then moves the primary_country__c value from the sales order item to the associated sales order’s primary_country__c field.

public with sharing class SalesOrderItemTriggerHandler {

  // update the primary country when new records are inserted from trigger
  public void OnAfterInsert(List newRecords){
  // update the primary country when records are updated from trigger  
  public void OnAfterUpdate(List oldRecords, 
      List updatedRecords,  Map oldMap, 
      Map newMap){
  // updates the sales order with the primary purchased country for the item
  private void updatePrimaryCountry(List newRecords) {
    // create a new map to hold the sales order id / country values
    Map salesOrderCountryMap = new Map();
    // if an item is marked as primary, add the purchased country
    // to the map where the sales order id is the key 
    for (Sales_Order_Item__c soi : newRecords) {
      if (soi.Primary_Item__c)
    // query for the sale orders in the context to update
    List orders = [select id, Primary_Country__c from Sales_Order__c 
      where id IN :salesOrderCountryMap.keyset()];
    // add the primary country to the sales order. find it in the map
    // using the sales order's id as the key
    for (Sales_Order__c so : orders)
      so.Primary_Country__c = salesOrderCountryMap.get(so.id);
    // commit the records 
    update orders;


Test_SalesOrderItemTriggerHandler (source on GitHub) – Test class for SalesOrderItemTrigger and SalesOrderItemTriggerHandler. Achieves 100% code coverage.

private class Test_SalesOrderItemTriggerHandler {

  private static Sales_Order__c so1;
  private static Sales_Order__c so2;

  // set up our data for each test method
  static {
  	Contact c = new Contact(firstname='test',lastname='test',email='no@email.com');
  	insert c;
    so1 = new Sales_Order__c(name='test1',Delivery_Name__c=c.id);
    so2 = new Sales_Order__c(name='test2',Delivery_Name__c=c.id);
    insert new List{so1,so2};

  static testMethod void testNewRecords() {
    Sales_Order_Item__c soi1 = new Sales_Order_Item__c();
    soi1.Sales_Order__c = so1.id;
    soi1.Quantity__c = 1;
    soi1.Description__c = 'test';
    soi1.Purchased_Country__c = 'Germany';
    Sales_Order_Item__c soi2 = new Sales_Order_Item__c();
    soi2.Sales_Order__c = so1.id;
    soi2.Quantity__c = 1;
    soi2.Description__c = 'test';
    soi2.Purchased_Country__c = 'France';
    soi2.Primary_Item__c = true;
    Sales_Order_Item__c soi3 = new Sales_Order_Item__c();
    soi3.Sales_Order__c = so2.id;
    soi3.Quantity__c = 1;
    soi3.Description__c = 'test';
    soi3.Purchased_Country__c = 'Germany';
    soi3.Primary_Item__c = true;
    Sales_Order_Item__c soi4 = new Sales_Order_Item__c();
    soi4.Sales_Order__c = so2.id;
    soi4.Quantity__c = 1;
    soi4.Description__c = 'test';
    soi4.Purchased_Country__c = 'Germany';
    Sales_Order_Item__c soi5 = new Sales_Order_Item__c();
    soi5.Sales_Order__c = so2.id;
    soi5.Quantity__c = 1;
    soi5.Description__c = 'test';
    soi5.Purchased_Country__c = 'Italy';
    insert new List{soi1,soi2,soi3,soi4,soi5}; 
    System.assertEquals(2,[select count() from Sales_Order_Item__c where Sales_Order__c = :so1.id]);
    System.assertEquals(3,[select count() from Sales_Order_Item__c where Sales_Order__c = :so2.id]); 
    System.assertEquals('France',[select primary_country__c from Sales_Order__c where id = :so1.id].primary_country__c);
    System.assertEquals('Germany',[select primary_country__c from Sales_Order__c where id = :so2.id].primary_country__c);
  static testMethod void testUpdatedRecords() {
    Sales_Order_Item__c soi1 = new Sales_Order_Item__c();
    soi1.Sales_Order__c = so1.id;
    soi1.Quantity__c = 1;
    soi1.Description__c = 'test';
    soi1.Purchased_Country__c = 'Germany';
    Sales_Order_Item__c soi2 = new Sales_Order_Item__c();
    soi2.Sales_Order__c = so1.id;
    soi2.Quantity__c = 1;
    soi2.Description__c = 'test';
    soi2.Purchased_Country__c = 'France';
    soi2.Primary_Item__c = true;
    insert new List{soi1,soi2}; 
    // assert that the country = France
    System.assertEquals('France',[select primary_country__c from Sales_Order__c where id = :so1.id].primary_country__c);
    List items = [select id, purchased_country__c from Sales_Order_Item__c 
      where Sales_Order__c = :so1.id and primary_item__c = true];
    // change the primary country on the sales order item. should trigger update
    items.get(0).purchased_country__c = 'Denmark';
    update items;
    // assert that the country was successfully changed to Denmark
    System.assertEquals('Denmark',[select primary_country__c from Sales_Order__c where id = :so1.id].primary_country__c);


Written by Jeff Douglas

August 23, 2011 at 9:00 am

Posted in Apex, Code Sample, Trigger

Video Tutorial – Developing with Apex REST Services

with 3 comments

I’ve had this video queued up for a few days now as I was waiting for the Apex REST API webinar. I didn’t want to steal Sandeep Bhanot’s and Alex Toussaint’s thunder. Their webinar was very informative and packed full of code! It should be available here any day now for your viewing pleasure.

Over at CloudSpokes we’ve been running development challenges with Apex REST services for quite awhile now. To facilitate future challenges I put together a video showing everything (yes, everything) you need to know to get started building applications with Apex REST services. The tutorial below walks you through the entire process of building our Members service. It shows you how to structure your services, how to use the new REST classes, and how to use the Apigee Console for Salesforce.com to test your services. The Apigee console even handles the OAuth for you!! Why write your own Java or Rails client app to test your classes? I’ve even published the entire code for the Members service so you can use it as a template. You can find the code here at github.

So here’s how to get started:

  1. Sign up for a new developer org at developer.force.com. All new orgs are now enabled with the Apex REST services so you can get started right away without waiting for the feature to get enabled.
  2. Take a peek at the Force.com REST API site for tutorials, code snippets, webinars and documentation. You can even post questions to the message boards for quick answers to your problems.
  3. Watch this awesome video by our friends at Apigee entitled RESTful API Design: Teach a Dog to REST. We love the design principles.
  4. Watch the video above for a quick tutorial services and then check out the code.
  5. Use the Apigee Console for Salesforce.com as your client application and start writing some code!
If you want some real-world experience using Apex REST services, hop on over to CloudSpokes and participate in some of our challenges to rebuild our site using Apex REST services. Prizes range from $500 – $1000 and we have a large number of challenges available!

Written by Jeff Douglas

July 21, 2011 at 8:55 am

Posted in CloudSpokes, REST

Tagged with

Overriding Standard Links & Buttons

leave a comment »

Salesforce.com allows you to override standard button and links (add, edit, delete, etc.) throughout the platform with your own customized functionality. This is a video which details some of the functionality I outlined here along with the new feature to by-pass the recordtype selection page.

Written by Jeff Douglas

May 2, 2011 at 9:00 am

Posted in Beginner, Configuration

Let’s Teach People Salesforce.com!!

leave a comment »

Genius Idea!Wes Nolte and I recently wrote the Salesforce Handbook. The book is doing well and people seem to really like it. However, it’s still a printed book with static content that must be updated to stay relevant (#fail). During the writing process Wes and I talked about a living, breathing version of the book to continue to teach, demonstrate best practices and provide sample code and configuration tips. However, we have families to feed and writing isn’t that profitable, let alone blogging.

I was flying home for San Francisco last week and was browsing the salesforce.com message boards (i.e., I have no life). There are still a ton of people asking basic questions like, “how do I test a trigger” or “how do I set a default value in my Visualforce page” or “why is this validation rule doing this ….”. Some of these questions have already been answered but they are scattered around a number of community blogs. Wouldn’t it be great if there was a centralized location to teach people salesforce.com? [insert magic light bulb ding sound]

So during my red-eye-flight-stupor, I decided to start this blog to help people learn saleforce.com. I’m inviting everyone in the salesforce.com community to contribute. If you have a workflow tip, some sample Apex code or an explanation on territority management that you’d like to share, I’ll add you as a contributor and you can post it. Perhaps you saw a great recipe on the Force.com Cookbook site. Post a link to it. You found a great Ruby book on Amazon.com. Post a link to it. A cool demo on YouTube. Post a link to it. Get the idea?

Can you post content that already exist on your blog? Not a problem as long as the goal is to educate the salesforce.com masses. Feel free to cross-post away! The more the merrier. I’ve put together a few simple guidelines to help us focus and provide value.

If you would like to contribute, simply send me an email at jeff[at]jeffdouglas(dot)com with your wordpress.com email address and I’ll add you. If you don’t already have a wordpress.com account you must create one first.

My hope is that people will be interested in this site and not only read it but provide content. We’ll see. Let the crowdsourcing learning experiment begin!

Written by Jeff Douglas

April 29, 2011 at 3:34 pm

Posted in Uncategorized