Mass Contact Transfer – Part 2 (The Controller)

Click on the SourceCode tab above to view the full sourcecode for the application or to install a complete working version into your environment.

In the last blog post I went into some of the details behind the Mass Contact Transfer VisualForce page. In this post, I’ll delve into the page Controller and supporting classes that contain the business logic for the page.

The transferContacts class is the controller for the VisualForce page. The controller has four main functions:

  • Provide the get/set methods for fields used on the VisualForce page
  • Create picklists of users for the To and From user fields
  • A doSearch() method that looks for records based on the criteria entered
  • A doTransfer() method to transfer the selected contacts to the new users

Get/Set methods

Generally these are very simple methods to either set the value of a public variable or return that value. Get/Set methods are always preceded by the word get or set followed by the name of the variable. On the VisualForce page these are referenced as {!fieldname}. For example, using value=”!fromUserID}” on a VisualForce page will call the getfromUserID() method to retrieve the value and setfromUserId() to set the value when the form is submitted.

// Variables
public string fromUserID;
public string toUserID;

// Get/Set Methods for the FROM and TO UserID's
public String getfromUserID()   { return fromUserID; }
public String gettoUserID()     { return toUserID; }
public void setfromUserID(string userID)  { this.fromUserID = userID; }
public void settoUserID(string userID)    { this.toUserID = userID; }

The From and To User picklist fields

Rather than require users to use a search lookup for users on the transfer form, I implemented this using two picklists for the From and To users. The FromUsers list includes all Users while the ToUsers lists only includes Active users. The setup behind the two picklists fields is very straight forward.

VisualForce page code for a picklist. The codeblock below accesses the getFromUsers() method to fill the values in the tag.

<apex:selectList value="{!fromUserID}" size="1" required="false"  id="fromUserID">
<apex:selectOptions value="{!FromUsers}"></apex:selectOptions>
</apex:selectList>

Apex Controller code to return the picklist of values. The getFromUsers() method returns a List object of Users sorted by Name. The SelectOption list type accepts two values – an ID and a display name. When a FromUser name is selected from the list, the Apex Controller will be able to access the ID of that user by referencing the toUserID variable. This is set by the page to the ID of the selected when the form is submitted.

public List<SelectOption> getFromUsers() {
    List<SelectOption> options = new List<SelectOption>();
    options.add(new selectOption('', '--- select a User ---'));
    for (User u : [Select ID, Name FROM User Order by Name]) {
            options.add(new selectOption(u.ID, u.Name));
    }
    return options;
}

Searching for Contacts

The doSearch() method is called when the [Find] button is clicked on the page.

<apex:commandButton title="Find" value="Find" action="{!doSearch}"/>

The overall goal of the method is to build an SOQL string, run the Query, and display a list of contacts on the page. As discussed in Part 1, the searchCriteria() class contains the methods necessary to generate the actual WHERE clause parts to build the query string. The logic behind this is shown below.

To start, a base SOQL string is built selecting all the fields in the Contact object along with key fields from the Account, Owner, Account.Owner, CreatedBy.Owner, and LastModifiedBy.Owner relationships. The initial contents of the WHERE exclude Contacts where the current owner is the owner to transfer contacts to (in other words, they own the contacts already).

// Build the base SOQL String, querying the standard Contact fields
// WHERE the current OwnerID = the selected value
string cSOQL = 'SELECT ' + contactFieldsList + ', Account.Name, Account.Site, Account.Owner.Name, '  +
'Account.Industry, Account.Type, Account.Owner.Alias, ' +
'Owner.Name, Owner.Alias, CreatedBy.Name, CreatedBy.Alias, LastModifiedBy.Name, LastModifiedBy.Alias ' +
'FROM Contact WHERE OwnerID <> \'' + toUserID + '\' ';

If a To Owner was selected, add to the WHERE clause to restrict the list of contacts owned by the To User ID.

// If a From User was selected, add this to the criteria
if (fromUserID <> null) cSOQL += ' AND OwnerID = \'' + fromUserID + '\' ';

For each line of Criteria, call the BuildWhereClause() method in the searchCriteria class.

// For each criteria line item, call the method to build the where clause component
for (searchCriteria cl : criteriaLine) {
cSOQL += cl.buildWhereClause(DebugMode);
}

Finally, order the list by Account Name and then Contact Name and limit the list to the first 250 contacts.

// Sort the results and limit to the first 250 rows
cSOQL += ' ORDER BY Account.Name, Name LIMIT 250' ;

searchCriteria class

The BuildWhereClause() to build the WHERE clause component for each of the search criteria lines is where it got complex. This method had to generate code to handle different field types (Text, PickList, Boolean, Number, Date, DateTime, etc.), different operators (=, >, <, contains, IN, etc.) and different types of values. Most of the logic is fairly straight forward, checking the field type and the operator and generating errors where the two are not compatible (BOOLEAN and ‘Less Than’, for example).

The complex part of the logic is to handle Date and DateTime field types. The most recent version of the application now supports the date format of the current user when parsing the value entered. Where this became tedious was for DateTime types. For example, you might expect the following SOQL WHERE block to only retrieve Contacts created on April 1, 2009: WHERE CreatedDate = 2009-04-01. However, CreatedDate is a DateTime field and requires a timestamp, not just a date. In order to properly query Contacts created on 4/1/09 the actual WHERE clause needs to looks like: WHERE (CreatedDate >= 2009-04-01T00:00:00Z AND CreatedDate <= 2009-04-01T23:59:59Z)

Transferring the Contacts

The final step in the process is to transfer the selected contacts to the ‘To User’. The toTransfer() method starts by building a list of Contact ID’s for selected Contacts. Part 1 of this blog post goes into the checkbox on each Contact. Only checked contacts are transferred. The list of ID’s is passed to a Query to select the Contacts. Finally, the OwnerID for each Contact is changed and the Database.Update() method is called to do the actual transfer.

// Build a list of Contact ID's to transfer ownership for
List<string> IDs = New List<string>();
for (transferContactSearchResults c : searchResults) {
if (c.selected) IDs.add(c.contact.ID) ;
}

// Query the contacts being transferred
List<Contact> contacts = [SELECT ID, OwnerID, Name, Account.Name, Title, Owner.Alias FROM Contact WHERE ID IN :IDs];
for (Contact c : contacts) {
c.ownerID = toUserID ;
}

// Process Errors and Count the Number of Records Transferred
Integer transferCount = 0;
List<database.saveresult> srs = database.update(contacts);
for (database.saveresult sr : srs) {
if (!sr.isSuccess()) {
      	ApexPages.AddMessage(new ApexPages.Message(ApexPages.Severity.FATAL, sr.getId() + '/' + sr.getErrors()[0].getMessage() ));
} else {
      	transferCount++;
      }
}

At the very end, the doSearch() method is called to look for additional contacts if there were more than 250 returned in the first query call.

Quick links to Class source code:

The final part of this blog post series will go into building the test class.