Archives

Apex Trigger for Sales Rep Scoring

This trigger is used to track and score sales rep activities as they complete Account fields.

Account Trigger Handler to Track specific field updates by sales rep and give points for based on which field was completed.

trigger AccountTrigger on Account (after delete, after insert, after undelete, after update, before delete, before insert, before update) {
    
    if (!System.isFuture() && !System.isBatch()){
      AccountTriggerHandler handler = new AccountTriggerHandler(Trigger.isExecuting, Trigger.size);

      if(Trigger.isUpdate && Trigger.isBefore){
          handler.OnBeforeUpdate(Trigger.old, Trigger.new, Trigger.newMap);
          AccountTriggerHandler.firstRun=false;
      }
}

Account Trigger Handler calls the SalesRepActivityScoring class to loop through all the accounts see which fields have been completed and assign points accordingly.

public with sharing class AccountTriggerHandler {
	
 	public static boolean firstRun = true; 

	private Boolean m_isExecuting = false;
    private Integer batchSize = 0;

	public AccountTriggerHandler(boolean isExecuting, integer size) {
		this.m_isExecuting = isExecuting;
        this.batchSize = size;
	}

	public void onBeforeUpdate(List<Account> oldAccounts, List<Account> updatedAccounts, Map<Id, Account> accountMap){
		SalesRepActivityScoring salesRepScoring = new SalesRepActivityScoring();
		salesRepScoring.insertSalesRepActivity(oldAccounts, updatedAccounts, accountMap);
	}
}

SalesRepActivityScoring class loops through accounts and checks if field not null and assign score. If field goes from filled in to null we subtract or points.

public with sharing class SalesRepActivityScoring {
	
	private static final Integer POTENTIALTOINVEST=4;
	private static final Integer LIQUIDASSETS=1;
	private static final Integer CONTEXTOFLC=1;
	private static final Integer GOALFORFUNDS=1;
	private static final Integer OCCUPATION=1;
	private static final Integer ACCREDIDTEDINVESTOR=1;
	private static final Integer HEADABOUTUS =1;

	private static final String ExecWithDialerProfileId = [Select Id from Profile where Name='Exec Standard user with dialer'].Id;
	private static final String AssociateWithDialerProfileId = [Select Id from Profile where Name='Associate Standard User With Dialer'].Id;

	public void insertSalesRepActivity(List<Account> oldAccounts, List<Account> updatedAccounts, Map<Id, Account> accountMap){
		List<SalesRep_Scoring__c> updateSalesRepScoringRecords = new List<SalesRep_Scoring__c>();

		Integer counter = 0;
		for (Account updatedAccount : updatedAccounts){
			if (updatedAccount.Account_Actor_ID__c!=null){
				if (UserInfo.getUserId().equals(updatedAccount.Lead_Assigned_To__c) && (UserInfo.getProfileId().equals(ExecWithDialerProfileId)) || UserInfo.getProfileId().equals(AssociateWithDialerProfileId)){
			
					SalesRep_Scoring__c salesRepScoring = new SalesRep_Scoring__c();
					salesRepScoring.Account__c = updatedAccount.Id;
					salesRepScoring.Account_Number__c = updatedAccount.Account_Actor_ID__c;
					salesRepScoring.Sales_Rep__c = UserInfo.getUserId();

					Boolean flag = false;

					if (updatedAccount.Accredited_Investor__c!=null){
						salesRepScoring.Accredited_Investor__c = ACCREDIDTEDINVESTOR;
						flag=true;
					} else if (oldAccounts.get(counter).Accredited_Investor__c!=null && updatedAccount.Accredited_Investor__c==null){
						salesRepScoring.Accredited_Investor__c = 0;
						flag=true;
					}

					if (updatedAccount.Context_of_LC_in_portfolio__c!=null){
						salesRepScoring.Context_of_LC__c = CONTEXTOFLC;
						flag=true;
					}else if (oldAccounts.get(counter).Context_of_LC_in_portfolio__c!=null && updatedAccount.Context_of_LC_in_portfolio__c==null) {
						salesRepScoring.Context_of_LC__c = 0;
						flag=true;
					}

					if (updatedAccount.Goal_for_Funds_at_LC__c!=null){
						salesRepScoring.Goal_for_funds__c = GOALFORFUNDS;
						flag=true;
					} else if (oldAccounts.get(counter).Context_of_LC_in_portfolio__c!=null && updatedAccount.Goal_for_Funds_at_LC__c==null){
						salesRepScoring.Goal_for_funds__c = 0;
						flag=true;
					}

					if (updatedAccount.How_did_you_hear_about_us__c!=null){
						salesRepScoring.How_did_you_hear_about_us__c = HEADABOUTUS;
						flag=true;
					} else if (oldAccounts.get(counter).How_did_you_hear_about_us__c!=null && updatedAccount.How_did_you_hear_about_us__c==null) {
						salesRepScoring.How_did_you_hear_about_us__c = 0;
						flag=true;
					}

					if (updatedAccount.Liquid_assets_client_stated__c!=null){
						salesRepScoring.Liquid_Assets__c = LIQUIDASSETS;
						flag=true;
					} else if (oldAccounts.get(counter).Liquid_assets_client_stated__c!=null && updatedAccount.Liquid_assets_client_stated__c==null){
						salesRepScoring.Liquid_Assets__c = 0;
						flag=true;
					}

					if (updatedAccount.Head_of_household_Occupation__c!=null){
						salesRepScoring.Occupation__c=OCCUPATION;
						flag=true;
					} else if (oldAccounts.get(counter).Head_of_household_Occupation__c!=null && updatedAccount.Head_of_household_Occupation__c==null){
						salesRepScoring.Occupation__c=0;
						flag=true;
					}

					if (updatedAccount.Potential_to_invest__c!=null){
						salesRepScoring.Potential_to_Invest__c=POTENTIALTOINVEST;
						flag=true;
					} else if (oldAccounts.get(counter).Potential_to_invest__c!=null && updatedAccount.Potential_to_invest__c==null) {
						salesRepScoring.Potential_to_Invest__c=0;
						flag=true;
					}

					if (flag){
						updateSalesRepScoringRecords.add(salesRepScoring);
					}
				}	
			}
			counter++;
		}
		upsert updateSalesRepScoringRecords Account_Number__c;
	}
}

SalesRepActivityScoringTest Class (100% code coverage). The first test method will test that the scoring only works for specific profiles. Second and third test method test scoring when fields completed for selected profiles. The last method will test if the scores is subtracted when fields goes from filled in to blank.

@isTest
private class SalesRepActivityScoringTest {
	

	public Id refProfileIdByName(String profileName){
        Profile getprofileId = [SELECT id FROM Profile WHERE name= :profileName];
        return getProfileId.id;
    }
    public User createAssociateSalesRepByProfile(String profileName){   
        User toUser = new User(alias = 'lcrep', 
        email='salesrep@lendingclub.com', 
        emailencodingkey='UTF-8',
        firstname='Associate', 
        lastname='Rep', 
        languagelocalekey='en_US', 
        localesidkey='en_US', 
        profileid = refProfileIdByName(profileName), 
        timezonesidkey='America/Los_Angeles', 
        username='assosciatesalesrep@lendingclub.com',
        Record_Count__c = 0,
        Assigned_Record_Count__c=10,
        Record_Tier__c='1;2;3;4;5;6;7;8;9;10',
        isActive=true);
        
        return toUser;
    }

    private Account createDummyAccount(User newUser, Decimal actorId){
    	Account newAccount = new Account();
		newAccount.Name='[TEST]AccountRepActivity';
		newAccount.Lead_Assigned_To__c=newUser.Id;
		newAccount.OwnerId= newUser.Id;
		newAccount.Account_Actor_ID__c=actorId;
		insert newAccount;

	
		return newAccount;
    }

	@isTest(SeeAllData=true) static void testSalesRepActivityScoringNotAddingForStandardRep() {
		SalesRepActivityScoringTest sra = new SalesRepActivityScoringTest();
		User standardUser = sra.createAssociateSalesRepByProfile('Standard User');
		insert standardUser;
		Account newAccount = sra.createDummyAccount(standardUser, 1111.0);

		Test.startTest();
			update newAccount;
		Test.stopTest();

		System.assertEquals([Select id from SalesRep_Scoring__c where Account__c=:newAccount.Id].size(), 0);
	}
	
	@isTest static void testSalesRepActivityScoringAddingForExecRep() {
		SalesRepActivityScoringTest sra = new SalesRepActivityScoringTest();
		User execUser = sra.createAssociateSalesRepByProfile('Exec Standard user with dialer');
		insert execUser;
		System.runAs(execUser){
			Account newAccount = sra.createDummyAccount(execUser, 1234.0);

			newAccount.Account_Actor_ID__c=1234.0;
			newAccount.Accredited_Investor__c='Not Accredited';
			newAccount.Context_of_LC_in_portfolio__c='a';
			newAccount.Goal_for_Funds_at_LC__c='a';
			newAccount.How_did_you_hear_about_us__c='a';
			newAccount.Liquid_assets_client_stated__c='a';
			newAccount.Head_of_household_Occupation__c='a';
			newAccount.Potential_to_invest__c='a';

			Test.startTest();
				update newAccount;
			Test.stopTest();

			SalesRep_Scoring__c salesRepScoring = [select Goal_for_funds__c,Account__c, Account_Number__c, Accredited_Investor__c, Context_of_LC__c, CreatedById, CreatedDate, How_did_you_hear_about_us__c, Id, IsDeleted, LastActivityDate, LastModifiedById, LastModifiedDate, Liquid_Assets__c, Name, Occupation__c, OwnerId, Potential_to_Invest__c, Sales_Rep__c, SystemModstamp from SalesRep_Scoring__c where Account__c=:newAccount.Id][0];

			System.assertEquals(salesRepScoring.Account__c, newAccount.Id);
			System.assertEquals(salesRepScoring.Sales_Rep__c, execUser.Id);
			System.assertEquals(salesRepScoring.Accredited_Investor__c, 1);
			System.assertEquals(salesRepScoring.Context_of_LC__c, 1);
			System.assertEquals(salesRepScoring.Goal_for_funds__c, 1);
			System.assertEquals(salesRepScoring.How_did_you_hear_about_us__c, 1);
			System.assertEquals(salesRepScoring.Liquid_Assets__c, 1);
			System.assertEquals(salesRepScoring.Occupation__c, 1);
			System.assertEquals(salesRepScoring.Potential_to_Invest__c, 4);
		}
	}

	@isTest(SeeAllData=true) static void testSalesRepActivityScoringAddingForAssociateRep() {
		SalesRepActivityScoringTest sra = new SalesRepActivityScoringTest();
		User associate = sra.createAssociateSalesRepByProfile('Associate Standard User With Dialer');
		insert associate;
		System.runAs(associate){
			
			Account newAccount = sra.createDummyAccount(associate, 1235.0);

			newAccount.Account_Actor_ID__c=1235.0;
			newAccount.Accredited_Investor__c='Not Accredited';
			newAccount.Context_of_LC_in_portfolio__c='a';
			newAccount.Goal_for_Funds_at_LC__c='a';
			newAccount.How_did_you_hear_about_us__c='a';
			newAccount.Liquid_assets_client_stated__c='a';
			newAccount.Head_of_household_Occupation__c='a';
			newAccount.Potential_to_invest__c='a';

			Test.startTest();
				update newAccount;
			Test.stopTest();

			SalesRep_Scoring__c salesRepScoring = [select Goal_for_funds__c,Account__c, Account_Number__c, Accredited_Investor__c, Context_of_LC__c, CreatedById, CreatedDate, How_did_you_hear_about_us__c, Id, IsDeleted, LastActivityDate, LastModifiedById, LastModifiedDate, Liquid_Assets__c, Name, Occupation__c, OwnerId, Potential_to_Invest__c, Sales_Rep__c, SystemModstamp from SalesRep_Scoring__c where Account__c=:newAccount.Id][0];

			System.assertEquals(salesRepScoring.Account__c, newAccount.Id);
			System.assertEquals(salesRepScoring.Sales_Rep__c, associate.Id);
			System.assertEquals(salesRepScoring.Accredited_Investor__c, 1);
			System.assertEquals(salesRepScoring.Context_of_LC__c, 1);
			System.assertEquals(salesRepScoring.Goal_for_funds__c, 1);
			System.assertEquals(salesRepScoring.How_did_you_hear_about_us__c, 1);
			System.assertEquals(salesRepScoring.Liquid_Assets__c, 1);
			System.assertEquals(salesRepScoring.Occupation__c, 1);
			System.assertEquals(salesRepScoring.Potential_to_Invest__c, 4);

		}
	}

	@isTest(SeeAllData=true)  static void testSalesRepActivityResetNullResetScore(){
		SalesRepActivityScoringTest sra = new SalesRepActivityScoringTest();
		User execUser = sra.createAssociateSalesRepByProfile('Exec Standard user with dialer');
		insert execUser;
		System.runAs(execUser){
			Account newAccount = sra.createDummyAccount(execUser, 1234.0);

			newAccount.Account_Actor_ID__c=1234.0;
			newAccount.Accredited_Investor__c='Not Accredited';
			newAccount.Context_of_LC_in_portfolio__c='a';
			newAccount.Goal_for_Funds_at_LC__c='a';
			newAccount.How_did_you_hear_about_us__c='a';
			newAccount.Liquid_assets_client_stated__c='a';
			newAccount.Head_of_household_Occupation__c='a';
			newAccount.Potential_to_invest__c='a';

			update newAccount;

			SalesRep_Scoring__c salesRepScoring = [select Goal_for_funds__c,Account__c, Account_Number__c, Accredited_Investor__c, Context_of_LC__c, CreatedById, CreatedDate, How_did_you_hear_about_us__c, Id, IsDeleted, LastActivityDate, LastModifiedById, LastModifiedDate, Liquid_Assets__c, Name, Occupation__c, OwnerId, Potential_to_Invest__c, Sales_Rep__c, SystemModstamp from SalesRep_Scoring__c where Account__c=:newAccount.Id][0];

			System.assertEquals(salesRepScoring.Account__c, newAccount.Id);
			System.assertEquals(salesRepScoring.Sales_Rep__c, execUser.Id);
			System.assertEquals(salesRepScoring.Accredited_Investor__c, 1);
			System.assertEquals(salesRepScoring.Context_of_LC__c, 1);
			System.assertEquals(salesRepScoring.Goal_for_funds__c, 1);
			System.assertEquals(salesRepScoring.How_did_you_hear_about_us__c, 1);
			System.assertEquals(salesRepScoring.Liquid_Assets__c, 1);
			System.assertEquals(salesRepScoring.Occupation__c, 1);
			System.assertEquals(salesRepScoring.Potential_to_Invest__c, 4);

			newAccount.Accredited_Investor__c=null;
			newAccount.Context_of_LC_in_portfolio__c=null;
			newAccount.Goal_for_Funds_at_LC__c=null;
			newAccount.How_did_you_hear_about_us__c=null;
			newAccount.Liquid_assets_client_stated__c=null;
			newAccount.Head_of_household_Occupation__c=null;
			newAccount.Potential_to_invest__c=null;

			Test.startTest();
				update newAccount;
			Test.stopTest();

			SalesRep_Scoring__c salesRepScoringReset = [select Goal_for_funds__c,Account__c, Account_Number__c, Accredited_Investor__c, Context_of_LC__c, CreatedById, CreatedDate, How_did_you_hear_about_us__c, Id, IsDeleted, LastActivityDate, LastModifiedById, LastModifiedDate, Liquid_Assets__c, Name, Occupation__c, OwnerId, Potential_to_Invest__c, Sales_Rep__c, SystemModstamp from SalesRep_Scoring__c where Account__c=:newAccount.Id][0];

			System.assertEquals(salesRepScoringReset.Account__c, newAccount.Id);
			System.assertEquals(salesRepScoringReset.Sales_Rep__c, execUser.Id);
			System.assertEquals(salesRepScoringReset.Accredited_Investor__c, 0);
			System.assertEquals(salesRepScoringReset.Context_of_LC__c, 0);
			System.assertEquals(salesRepScoringReset.Goal_for_funds__c, 0);
			System.assertEquals(salesRepScoringReset.How_did_you_hear_about_us__c, 0);
			System.assertEquals(salesRepScoringReset.Liquid_Assets__c, 0);
			System.assertEquals(salesRepScoringReset.Occupation__c, 0);
			System.assertEquals(salesRepScoringReset.Potential_to_Invest__c, 0);
		}
	}
}

Apex Code: Track Sales Rep Tasks and Set Follow Up Dates

public with sharing class SalesRepActivity  {
	
	public void updateSalesRepActivity(Set<Id> taskIds){
		try{
		
		for (Task task : [Select Id, Type, Subject, Status, AccountId, Account.Owner.Id, OwnerId, Detailed_Type__c from Task where Id IN : taskIds]){
			if (task.AccountId != null){
				if (task.Account.Owner.Id == task.OwnerId){
					Account accountToUpdate = new Account(Id=task.AccountId);

					if (task.type.equals('Sent Email') && task.subject.containsIgnoreCase('Mass Email:')){
						accountToUpdate.Last_Mass_Email__c = Date.today();
					}
					else if ((task.type.equals('Sent Email') || task.type.equals('Clearslide Email Pitch')) && task.subject.containsIgnoreCase('Email:')){
						accountToUpdate.Last_Non_Mass_Email__c = Date.today();
					}
					else if ((task.type.equals('Sent Email') && task.subject.containsIgnoreCase('re:')) && (task.Detailed_Type__c.equals('Connected') || task.Detailed_Type__c.equals('Meet') || task.Detailed_Type__c.equals('Meet outside'))){
						accountToUpdate.Last_Email_Connect__c = Date.today();
						accountToUpdate.Last_connected_date__c = Date.today();
					}
					else if (task.type.equals('Outbound Call') && task.status.equals('Completed') && task.Detailed_Type__c.equals('Connected')){
						accountToUpdate.Last_Call_Connect__c = Date.today();
						accountToUpdate.Last_connected_date__c = Date.today();
						accountToUpdate.Last_Call_attempt__c = Date.today();
					}
					else if (task.type.equals('Outbound Call')){
						accountToUpdate.Last_Call_attempt__c = Date.today();
					}
					else if (task.type.equals('Inbound Call')){
						accountToUpdate.Last_Call_attempt__c = Date.today();
						accountToUpdate.Last_connected_date__c = Date.today();
						accountToUpdate.Last_Call_Connect__c = Date.today();
					}

					update accountToUpdate;
				}
			}
		}

		}catch(Exception e){
			System.debug(e.getMessage());
		}
	}
}

Apex Update Case Priority when Bad Words in Incoming Email

Updating a case priority of a case when you receive a email with some words in the body that will make it more important. By looking at the words you can update the priority to high so Support reps can faster attend to those cases

public with sharing class CaseMatchIncomingEmail {
	public enum badWords {
		Frustrated,
		Frustrating,
		Angry,
		Mad,
		Annoyed,
		Annoying,
		Complain,
		Complaint,
		Lost,
		Upset,
		Awful,
		Bad,
		Mislead,
		Misleading,
		Fraud,
		Scam,
		request,
		SEC,
		transfer,
		Broken,
		Problem,
		Issue,
		Disappoint,
		Disappointing,
		Prosper,
		Lawsuit,
		Sue,
		Hate,
		Attorney,
		Discrepancy,
		Pissed,
		ridiculous,
		immediate,
		immediately,
		kidding,
		joking,
		joke}

	public void mathIncomingCaseEmail(List<Case> casesToUpdate) {
		List<Case> caseToUpdateList = new List<Case>();

		for (Case caseToUpdate : casesToUpdate){
			for (badWords badword : badWords.values()){
				if (caseToUpdate.Subject!=null){
					if (caseToUpdate.Subject.containsIgnoreCase(badword.name())){
						caseToUpdate.Priority = 'High';
					}
				}
				if (caseToUpdate.Description!=null){
					if (caseToUpdate.Description.containsIgnoreCase('disclaimer')){
						if (caseToUpdate.Description.lastIndexOf('disclaimer')>0){
							if (caseToUpdate.Description.substring(0,caseToUpdate.Description.lastIndexOf('disclaimer')).containsIgnoreCase(badword.name())){
							caseToUpdate.Priority = 'High';
							}
						}
					}
					else if (caseToUpdate.Description.containsIgnoreCase(badword.name())){
						caseToUpdate.Priority = 'High';
					}
				}
			}

			if (caseToUpdate.SuppliedEmail != null){
				List<List<SObject>> searchList = searchByField(caseToUpdate.SuppliedEmail);
				List<Contact> contactSearchList =  ((List<Contact>)searchList[0]);
				List<Admin_Member_ID_Account__c> adminMemberSearchList =  ((List<Admin_Member_ID_Account__c>)searchList[1]);

				if (contactSearchList.size() > 0){
					for (Contact contactSearchResult:contactSearchList){
						if (contactSearchResult.Email == caseToUpdate.SuppliedEmail){
							caseToUpdate.ContactId = contactSearchList.get(0).Id;
							caseToUpdate.AccountId = contactSearchList.get(0).AccountId;
						}
					}
				}
				if (adminMemberSearchList.size() > 0){
					for (Admin_Member_ID_Account__c adminMemberIdAccount : adminMemberSearchList){
						if (adminMemberIdAccount.AM_ID_Email__c == caseToUpdate.SuppliedEmail)
							caseToUpdate.AM_Member_ID__c = adminMemberSearchList.get(0).Id;
					}
				}
			}
		}
	}
}

SalesIO JQuery Mobile Force.com Mobile Application

SalesIO is Force.com JQuery Mobile Iphone App provides all the functionality that a busy sales rep will need to easily manage their day to day lifes. SalesIO provide:

  • Manage the basic Sales SObjects : Leads, Contacts, Accounts, Opportunities
  • Manage the basic Support SObject: Cases
  • Connect with customers using SMS and Email and Chatter
  • Graphs at your fingertips
  • Setup meetings and task
  • Organize notes and make todo lists











<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /><script type="text/javascript">// <![CDATA[

            var leadRecs = new Array(); 
            var contactRecs = new Array();
            var opportunityRecs = new Array();
            var accountRecs = new Array();
            var caseRecs = new Array();

            var restAPIClient = new forcetk.Client();
            restAPIClient.setSessionToken('{!$Api.Session_ID}');

            $j(document).ready(function() {
                regBtnClickHandlersLead();
                regBtnClickHandlersContact();
                regBtnClickHandlersAccount();
                regBtnClickHandlersOpportunity();
                regBtnClickHandlersChatter();
                regBtnClickHandlersEvent();
                regBtnClickHandlersCases();
                getAllLeads();
                getAllContacts();
                getAllAccounts();
                getAllOpportunities();
                getAllCases();
            });

           function getAllLeads(){
                $j.mobile.showPageLoadingMsg();
                var q = "select Id, LastName, FirstName, Phone, MobilePhone, Email, Company, Description, LeadSource, Status, Industry from Lead where FirstName!=null order by FirstName limit 200";
                restAPIClient.query(q , 
                            function(response) { 
                                   showLeads(response.records); 
                                }); 
            }

            function getAllContacts(){
                $j.mobile.showPageLoadingMsg();
                var q = "select Id, firstName, lastName, phone from Contact where FirstName!=null order by CreatedDate DESC limit 200";
                restAPIClient.query(q , 
                            function(response) { 
                                   showContacts(response.records); 
                                }); 
            }

            function getAllAccounts(){
                $j.mobile.showPageLoadingMsg();
                var q = "select Id, Name, BillingStreet, BillingCity, BillingState, BillingPostalCode, BillingCountry, Phone, Fax, Website from Account where Name!=null order by CreatedDate DESC limit 200";
                restAPIClient.query(q , 
                            function(response) { 
                                   showAccounts(response.records); 
                                }); 
            }

            function getAllOpportunities(){
                $j.mobile.showPageLoadingMsg();
                var q = "select Id, Name, Description, StageName, Amount, Probability, ExpectedRevenue, CloseDate from Opportunity where Name!=null order by CreatedDate DESC limit 200";
                restAPIClient.query(q , 
                            function(response) { 
                                   showOpportunities(response.records); 
                                }); 
            }

            function getAllCases(){
                $j.mobile.showPageLoadingMsg();
                var q = "select Id, Status, Reason, Origin, Subject, Description from Case order by CreatedDate DESC limit 200";
                restAPIClient.query(q , 
                            function(response) { 
                                   showCases(response.records); 
                                }); 
            }

            function showLeads(records)
            {
                 $j('#lList').empty();
                contactRecs.length = 0;

                for(var i = 0; i < records.length; i++) { leadRecs[records[i].Id] = records[i]; }
                var x = 0;
                $j.each(records,
                    function() {
                    var newLi = $j('
	<li></li>

');

                    var newLink = $j('<a id="' +this.Id+ '" data-transition="flip">' +this.FirstName+ ' '+this.LastName+ '</a>');
                    newLink.click(function(e) {
                        e.preventDefault();
                        $j.mobile.showPageLoadingMsg();
                        $j('#lfName').val(leadRecs[this.id].FirstName);
                        $j('#llName').val(leadRecs[this.id].LastName);
                        $j('#lphone').val(leadRecs[this.id].Phone);
                        $j('#lemail').val(leadRecs[this.id].Email);
                        $j('#lcompany').val(leadRecs[this.id].Company);
                        $j('#leadId').val(leadRecs[this.id].Id);
                        $j('#error').html('');

                        $j.mobile.changePage('#ldetailpage', {changeHash: true});
                    });
                    newLi.append(newLink);            
                    newLi.appendTo('#lList');
                    x++;
                  });

                $j.mobile.hidePageLoadingMsg();
                $j('#lList:visible').listview('refresh');
            }

            function showContacts(records) {    
                $j('#cList').empty();
                contactRecs.length = 0;

                for(var i = 0; i < records.length; i++) { contactRecs[records[i].Id] = records[i]; }
                var x = 0;
                $j.each(records,
                    function() {
                    var newLi = $j('
	<li></li>

');

                    var newLink = $j('<a id="' +this.Id+ '" data-transition="flip">' +this.FirstName+ ' '+this.LastName+ '</a>');
                    newLink.click(function(e) {
                        e.preventDefault();
                        $j.mobile.showPageLoadingMsg();
                        $j('#cfName').val(contactRecs[this.id].FirstName);
                        $j('#clName').val(contactRecs[this.id].LastName);
                        $j('#cphone').val(contactRecs[this.id].Phone);
                        $j('#contactId').val(contactRecs[this.id].Id);
                        $j('#error').html('');

                        $j.mobile.changePage('#cdetailpage', {changeHash: true});
                    });
                    newLi.append(newLink);            
                    newLi.appendTo('#cList');
                    x++;
                  });

                $j.mobile.hidePageLoadingMsg();
                $j('#cList:visible').listview('refresh');
            } 

             function showAccounts(records)
            {
                $j('#aList').empty();
                contactRecs.length = 0;

                for(var i = 0; i < records.length; i++) { accountRecs[records[i].Id] = records[i]; }
                var x = 0;
                $j.each(records,
                    function() {
                    var newLi = $j('
	<li></li>

');

                    var newLink = $j('<a id="' +this.Id+ '" data-transition="flip">' +this.Name+ '</a>');
                    newLink.click(function(e) {
                        e.preventDefault();
                        $j.mobile.showPageLoadingMsg();
                        $j('#afName').val(accountRecs[this.id].Name);
                        $j('#aphone').val(accountRecs[this.id].Phone);
                        $j('#accountId').val(accountRecs[this.id].Id);
                        $j('#error').html('');

                        $j.mobile.changePage('#adetailpage', {changeHash: true});
                    });
                    newLi.append(newLink);            
                    newLi.appendTo('#aList');
                    x++;
                  });

                $j.mobile.hidePageLoadingMsg();
                $j('#aList:visible').listview('refresh');
            }     

             function showOpportunities(records)
            {
                $j('#oList').empty();
                contactRecs.length = 0;

                for(var i = 0; i < records.length; i++) { opportunityRecs[records[i].Id] = records[i]; }
                var x = 0;
                $j.each(records,function() {
                    var newLi = $j('
	<li></li>

');
                    var newLink = $j('<a id="' +this.Id+ '" data-transition="flip">' +this.Name+ '</a>');
                    newLink.click(function(e) {
                        e.preventDefault();
                        $j.mobile.showPageLoadingMsg();
                        $j('#ofName').val(opportunityRecs[this.id].Name);
                        $j('#ostageName').val(opportunityRecs[this.id].StageName);
                        $j('#oamount').val(opportunityRecs[this.id].Amount);
                        $j('#oclosedate').val(opportunityRecs[this.id].CloseDate);
                        $j('#opportunityId').val(opportunityRecs[this.id].Id);
                        $j('#error').html('');

                        $j.mobile.changePage('#odetailpage', {changeHash: true});
                    });
                    newLi.append(newLink);            
                    newLi.appendTo('#oList');
                    x++;
                  });

                $j.mobile.hidePageLoadingMsg();
                $j('#oList:visible').listview('refresh');
            }

             function showCases(records)
            {
                $j('#csList').empty();
                contactRecs.length = 0;

                for(var i = 0; i < records.length; i++) { caseRecs[records[i].Id] = records[i]; }
                var x = 0;
                $j.each(records,
                    function() {
                    var newLi = $j('
	<li></li>

');

                    var newLink = $j('<a id="' +this.Id+ '" data-transition="flip">' +this.Subject + '</a>');
                    newLink.click(function(e) {
                        e.preventDefault();
                        $j.mobile.showPageLoadingMsg();
                        $j('#csStatus').val(caseRecs[this.id].Status);
                        $j('#csReason').val(caseRecs[this.id].Reason);
                        $j('#csOrigin').val(caseRecs[this.id].Origin);
                        $j('#csSubject').val(caseRecs[this.id].Subject);
                        $j('#csDescription').val(caseRecs[this.id].Description);
                        $j('#caseId').val(caseRecs[this.id].Id);
                        $j('#error').html('');

                        $j.mobile.changePage('#csdetailpage', {changeHash: true});
                    });
                    newLi.append(newLink);            
                    newLi.appendTo('#csList');
                    x++;
                  });

                $j.mobile.hidePageLoadingMsg();
                $j('#csList:visible').listview('refresh');
            }

            function displayError(e){
                var error = JSON.parse(e.responseText);
                $j('#error').html(error[0].message);
            }

            function regBtnClickHandlersLead() {
                $j('#addlead').click(function(e) {
                    e.preventDefault();
                    $j.mobile.showPageLoadingMsg();
                    $j('#lfName').val('');
                    $j('#llName').val('');
                    $j('#lphone').val('');
                    $j('#lemail').val('');
                    $j('#lcompany').val('');
                    $j('#error').html('');
                    $j('#leadId').val('');
                    $j.mobile.changePage('#ldetailpage', {changeHash: true});
                    $j.mobile.hidePageLoadingMsg();            
                });

                $j('#savelead').click(function(e) {
                   addUpdateLead(e);
                });

                $j('#deletelead').click(function(e) {
                   deleteLead(e);
                });
            }

            function addUpdateLead(e){
                e.preventDefault();
                var lId = $j('#leadId').val();
                var lead = { FirstName : $j('#lfName').val(),
                                LastName : $j('#llName').val(),
                                Phone : $j('#lphone').val(),
                                Email : $j('#lemail').val(),
                                Company : $j('#lcompany').val()};
                if (lId === 'undefined' || lId === ''){
                    restAPIClient.create('Lead', lead, sucessCallbackLead, displayError); 
                } else {
                    restAPIClient.update('Lead', lId, lead, sucessCallbackLead, displayError); 
                }
            }

            function deleteLead(e){
                e.preventDefault();
                restAPIClient.del('Lead', $j('#leadId').val(), sucessCallbackLead, displayError);
            }

            function sucessCallbackLead(r){
                $j.mobile.changePage('#Lead', {changeHash: true});
            }

             function regBtnClickHandlersContact() {
                $j('#addcontact').click(function(e) {
                    e.preventDefault();
                    $j.mobile.showPageLoadingMsg();
                    $j('#cfName').val('');
                    $j('#clName').val('');
                    $j('#cphone').val('');
                    $j('#error').html('');
                    $j('#contactId').val('');
                    $j.mobile.changePage('#cdetailpage', {changeHash: true});
                    $j.mobile.hidePageLoadingMsg();            
                });

                $j('#savecontact').click(function(e) {
                   addUpdateContact(e);
                });

                $j('#deletecontact').click(function(e) {
                   deleteContact(e);
                });
            }

             function addUpdateContact(e){
                e.preventDefault();
                var cId = $j('#contactId').val();
                var contact = { FirstName : $j('#cfName').val(),
                                LastName : $j('#clName').val(),
                                Phone : $j('#cphone').val()};
                if (cId === 'undefined' || cId === ''){
                    restAPIClient.create('Contact', contact, sucessCallbackContact, displayError); 
                } else {
                    restAPIClient.update('Contact', cId, contact, sucessCallbackContact, displayError); 
                }
            }

            function deleteContact(e){
                e.preventDefault();
                restAPIClient.del('Contact', $j('#contactId').val(), sucessCallbackContact, displayError);
            }

            function sucessCallbackContact(r){
                $j.mobile.changePage('#Contact', {changeHash: true});
            }

             function regBtnClickHandlersAccount() {
                $j('#addaccount').click(function(e) {
                    e.preventDefault();
                    $j.mobile.showPageLoadingMsg();
                    $j('#afName').val('');
                    $j('#aphone').val('');
                    $j('#error').html('');
                    $j('#accountId').val('');
                    $j.mobile.changePage('#adetailpage', {changeHash: true});
                    $j.mobile.hidePageLoadingMsg();            
                });

                $j('#saveaccount').click(function(e) {
                   addUpdateAccount(e);
                });

                $j('#deleteaccount').click(function(e) {
                   deleteAccount(e);
                });
            }

            function addUpdateAccount(e){
                e.preventDefault();
                var aId = $j('#accountId').val();
                var account = { Name : $j('#afName').val(),
                                Phone : $j('#aphone').val()};
                if (aId === 'undefined' || aId === ''){
                    restAPIClient.create('Account', account, sucessCallbackAccount, displayError); 
                } else {
                    restAPIClient.update('Account', aId, account, sucessCallbackAccount, displayError); 
                }
            }

            function deleteAccount(e){
                e.preventDefault();
                restAPIClient.del('Account', $j('#accountId').val(), sucessCallbackAccount, displayError);
            }

            function sucessCallbackAccount(r){
                $j.mobile.changePage('#Account', {changeHash: true});
            }

            function regBtnClickHandlersOpportunity() {
                $j('#addopportunity').click(function(e) {
                    e.preventDefault();
                    $j.mobile.showPageLoadingMsg();
                    $j('#ofName').val('');
                    $j('#ostageName').val('');
                    $j('#oclosedate').val('');
                    $j('#oamount').val('');
                    $j('#error').html('');
                    $j('#opportunityId').val('');
                    $j.mobile.changePage('#odetailpage', {changeHash: true});
                    $j.mobile.hidePageLoadingMsg();            
                });

                $j('#saveopportunity').click(function(e) {
                   addUpdateOpportunity(e);
                });

                $j('#deleteopportunity').click(function(e) {
                   deleteOpportunity(e);
                });
            }

             function addUpdateOpportunity(e){
                e.preventDefault();
                var oId = $j('#opportunityId').val();
                var opportunity = { Name : $j('#ofName').val(),
                                StageName : $j('#ostageName').val(),
                                CloseDate : $j('#oclosedate').val(),
                                Amount : $j('#oamount').val()};
                if (oId === 'undefined' || oId === ''){
                    restAPIClient.create('Opportunity', opportunity, sucessCallbackOpportunity, displayError); 
                } else {
                    restAPIClient.update('Opportunity', oId, opportunity, sucessCallbackOpportunity, displayError); 
                }
            }

            function deleteOpportunity(e){
                e.preventDefault();
                restAPIClient.del('Opportunity', $j('#opportunityId').val(), sucessCallbackOpportunity, displayError);
            }

            function sucessCallbackOpportunity(r){
                $j.mobile.changePage('#Opportunity', {changeHash: true});
            }

             function regBtnClickHandlersCases() {
                $j('#addcase').click(function(e) {
                    e.preventDefault();
                    $j.mobile.showPageLoadingMsg();
                    $j('#csStatus').val('');
                    $j('#csReason').val('');
                    $j('#csOrigin').val('');
                    $j('#csSubject').val('');
                    $j('#csDescription').val('');
                    $j('#error').html('');
                    $j('#caseId').val('');
                    $j.mobile.changePage('#csdetailpage', {changeHash: true});
                    $j.mobile.hidePageLoadingMsg();            
                });

                $j('#savecase').click(function(e) {
                   addUpdateCase(e);
                });

                $j('#deletecase').click(function(e) {
                   deleteCase(e);
                });
            }

             function addUpdateCase(e){
                e.preventDefault();
                var csId = $j('#caseId').val();
                var caseObj = {    Status : $j('#csStatus').val(),
                                Reason : $j('#csReason').val(),
                                Origin : $j('#csOrigin').val(),
                                Subject : $j('#csSubject').val(),
                                Description : $j('#csDescription').val()};
                if (csId === 'undefined' || csId === ''){
                    restAPIClient.create('Case', caseObj, sucessCallbackCase, displayError); 
                } else {
                    restAPIClient.update('Case', csId, caseObj, sucessCallbackCase, displayError); 
                }
            }

            function deleteCase(e){
                e.preventDefault();
                restAPIClient.del('Case', $j('#caseId').val(), sucessCallbackCase, displayError);
            }

            function sucessCallbackCase(r){
                $j.mobile.changePage('#Case', {changeHash: true});
            }

            function regBtnClickHandlersChatter() {

                $j('#savechatter').click(function(e) {
                   addChatter(e);
                });
            }

            function addChatter(e){
                e.preventDefault();
                var chatter = { title : $j('#chattertitle').val(),
                                body : $j('#chatterbody').val(),
                                ParentId : $j('#cwhoId').val()};
                restAPIClient.create('FeedItem', chatter, sucessCallbackLead, displayError); 
            }

            function emailFunction()
            {
                sendMail();
            }

            function smsFunction()
            {
                sendSMS();
            }

            function regBtnClickHandlersEvent() {
                $j('#saveevent').click(function(e) {
                   addUpdateEvent(e);
                });
            }

             function addUpdateEvent(e){
                e.preventDefault();
                var event = { WhoId : $j('#ewhoId').val(),
                                Subject : $j('#esubject').val(),
                                StartDateTime : $j('#estartdatetime').val(),
                                EndDateTime : $j('#eenddatetime').val(),
                                Description : $j('#edescription').val()};

                restAPIClient.create('Event', event, sucessCallbackLead, displayError); 
            }

            function regBtnClickHandlersTask() {
                $j('#savetask').click(function(e) {
                   addUpdateTask(e);
                });
            }

             function addUpdateTask(e){
                e.preventDefault();
                var task =     { WhoId : $j('#twhoId').val(),
                                Subject : $j('#tsubject').val(),
                                Priority : $j('#tpriority').val(),
                                Status : $j('#tstatus').val(),
                                Description : $j('#tcomments').val()};

                restAPIClient.create('Task', task, sucessCallbackLead, displayError); 
            }

// ]]></script></pre>
<div class="jqm-demos jqm-demos-index" id="Lead" data-role="page">
<div class="jqm-header" data-role="header">
<h1 class="jqm-logo"><a href="#"><img alt="SalesIO" src="{!URLFOR($Resource.SalesIOLogo, '')}" /></a></h1>
 <a class="jqm-navmenu-link" href="#" data-icon="bars" data-iconpos="notext">Navigation</a></div>
<!-- /header -->
<div class="jqm-content" id="leadList" data-role="content"></div>
<!-- /content -->

<div data-role="footer" data-id="foo1" data-position="fixed">
<div data-role="navbar">
<ul>
	<li><a class="ui-btn-active ui-state-persist" id="addlead" data-prefetch="true" data-transition="none" data-icon="grid"></a>New</li>
</ul>
</div>
<!-- /navbar --></div>
<!-- /footer --></div>
<pre>
<!-- /page --></pre>
<div class="jqm-demos jqm-demos-index" id="ldetailpage" data-role="page">
<div class="jqm-header" data-role="header">
<h1 class="jqm-logo"><a href="#"><img alt="SalesIO" src="{!URLFOR($Resource.SalesIOLogo, '')}" /></a></h1>
 <a class="jqm-navmenu-link" href="#" data-icon="bars" data-iconpos="notext">Navigation</a></div>
<!-- /header -->
<div class="jqm-content" data-role="content">
<div data-role="fieldcontain"><label for="lfName">First Name:</label>
 <input id="lfName" type="text" name="lfName" /></div>
<div data-role="fieldcontain"><label for="llName">Last Name:</label>
 <input id="llName" type="text" name="llName" /></div>
<div data-role="fieldcontain"><label for="lcompany">Company:</label>
 <input id="lcompany" type="text" name="lcompany" /></div>
<div data-role="fieldcontain"><label for="lphone">Phone:</label>
 <input id="lphone" type="text" name="lphone" /></div>
<div data-role="fieldcontain"><label for="lemail">Email:</label>
 <input id="lemail" type="text" name="lemail" /></div>
<h2 id="error" style="color: red;"></h2>
 <input id="leadId" type="hidden" />
 <button class="save" id="savelead" data-role="button" data-icon="check" data-inline="true" data-theme="b">Save</button>
 <button class="destroy" id="deletelead" data-role="button" data-icon="delete" data-inline="true">Delete</button></div>
</div>
<div class="jqm-demos jqm-demos-index" id="Contact" data-role="page">
<div class="jqm-header" data-role="header">
<h1 class="jqm-logo"><a href="#"><img alt="SalesIO" src="{!URLFOR($Resource.SalesIOLogo, '')}" /></a></h1>
 <a class="jqm-navmenu-link" href="#" data-icon="bars" data-iconpos="notext">Navigation</a></div>
<!-- /header -->
<div class="jqm-content" id="contactList" data-role="content"></div>
<!-- /content -->


<div data-role="footer" data-id="foo1" data-position="fixed">
<div data-role="navbar">
<ul>
	<li><a class="ui-btn-active ui-state-persist" id="addcontact" data-prefetch="true" data-transition="none" data-icon="grid"></a>New</li>
</ul>
</div>
<!-- /navbar --></div>
<!-- /footer --></div>
<div class="jqm-demos jqm-demos-index" id="cdetailpage" data-role="page">
<div class="jqm-header" data-role="header">
<h1 class="jqm-logo"><a href="#"><img alt="SalesIO" src="{!URLFOR($Resource.SalesIOLogo, '')}" /></a></h1>
 <a class="jqm-navmenu-link" href="#" data-icon="bars" data-iconpos="notext">Navigation</a></div>
<!-- /header -->
<div class="jqm-content" data-role="content">
<div data-role="fieldcontain"><label for="cfName">First Name:</label>
 <input id="cfName" type="text" name="cfName" /></div>
<div data-role="fieldcontain"><label for="clName">Last Name:</label>
 <input id="clName" type="text" name="clName" /></div>
<div data-role="fieldcontain"><label for="cphone">Phone:</label>
 <input id="cphone" type="text" name="cphone" /></div>
<h2 id="error" style="color: red;"></h2>
 <input id="contactId" type="hidden" />
 <button class="save" id="savecontact" data-role="button" data-icon="check" data-inline="true" data-theme="b">Save</button>
 <button class="destroy" id="deletecontact" data-role="button" data-icon="delete" data-inline="true">Delete</button></div>
</div>
<div class="jqm-demos jqm-demos-index" id="Account" data-role="page">
<div class="jqm-header" data-role="header">
<h1 class="jqm-logo"><a href="#"><img alt="SalesIO" src="{!URLFOR($Resource.SalesIOLogo, '')}" /></a></h1>
 <a class="jqm-navmenu-link" href="#" data-icon="bars" data-iconpos="notext">Navigation</a></div>
<!-- /header -->
<div class="jqm-content" id="accountList" data-role="content"></div>
<!-- /content -->


<div data-role="footer" data-id="foo1" data-position="fixed">
<div data-role="navbar">
<ul>
	<li><a class="ui-btn-active ui-state-persist" id="addaccount" data-prefetch="true" data-transition="none" data-icon="grid"></a>New</li>
</ul>
</div>
<!-- /navbar --></div>
<!-- /footer --></div>
<div class="jqm-demos jqm-demos-index" id="adetailpage" data-role="page">
<div class="jqm-header" data-role="header">
<h1 class="jqm-logo"><a href="#"><img alt="SalesIO" src="{!URLFOR($Resource.SalesIOLogo, '')}" /></a></h1>
 <a class="jqm-navmenu-link" href="#" data-icon="bars" data-iconpos="notext">Navigation</a></div>
<!-- /header -->
<div class="jqm-content" data-role="content">
<div data-role="fieldcontain"><label for="afName">First Name:</label>
 <input id="afName" type="text" name="afName" /></div>
<div data-role="fieldcontain"><label for="aphone">Phone:</label>
 <input id="aphone" type="text" name="aphone" /></div>
<h2 id="error" style="color: red;"></h2>
 <input id="accountId" type="hidden" />
 <button class="save" id="saveaccount" data-role="button" data-icon="check" data-inline="true" data-theme="b">Save</button>
 <button class="destroy" id="deleteaccount" data-role="button" data-icon="delete" data-inline="true">Delete</button></div>
</div>
<div class="jqm-demos jqm-demos-index" id="Opportunity" data-role="page">
<div class="jqm-header" data-role="header">
<h1 class="jqm-logo"><a href="#"><img alt="SalesIO" src="{!URLFOR($Resource.SalesIOLogo, '')}" /></a></h1>
 <a class="jqm-navmenu-link" href="#" data-icon="bars" data-iconpos="notext">Navigation</a></div>
<!-- /header -->
<div class="jqm-content" id="opportunityList" data-role="content"></div>
<!-- /content -->

<div data-role="footer" data-id="foo1" data-position="fixed">
<div data-role="navbar">
<ul>
	<li><a class="ui-btn-active ui-state-persist" id="addopportunity" data-prefetch="true" data-transition="none" data-icon="grid"></a>New</li>
</ul>
</div>
<!-- /navbar --></div>
<!-- /footer --></div>
<pre>
<!-- /page --></pre>
<div class="jqm-demos jqm-demos-index" id="odetailpage" data-role="page">
<div class="jqm-header" data-role="header">
<h1 class="jqm-logo"><a href="#"><img alt="SalesIO" src="{!URLFOR($Resource.SalesIOLogo, '')}" /></a></h1>
 <a class="jqm-navmenu-link" href="#" data-icon="bars" data-iconpos="notext">Navigation</a></div>
<!-- /header -->
<div class="jqm-content" data-role="content">
<div data-role="fieldcontain"><label for="ofName">Name:</label>
 <input id="ofName" type="text" name="ofName" /></div>
<div data-role="fieldcontain"><label for="ostageName">Stage Name:</label>
<select id="ostageName" name="ostageName"><option value="Closed Won">Closed Won</option><option value="Prospecting">Prospecting</option><option value="Qualification">Qualification</option><option value="Closed Lost">Closed Lost</option></select></div>
<div data-role="fieldcontain"><label for="oclosedate">Close Date:</label>
 <input id="oclosedate" type="date" name="oclosedate" data-role="datebox" data-options="{&quot;mode&quot;: &quot;calbox&quot;}" /></div>
<div data-role="fieldcontain"><label for="oamount">Amount:</label>
 <input id="oamount" type="text" name="oamount" /></div>
<h2 id="error" style="color: red;"></h2>
 <input id="opportunityId" type="hidden" />
 <button class="save" id="saveopportunity" data-role="button" data-icon="check" data-inline="true" data-theme="b">Save</button>
 <button class="destroy" id="deleteopportunity" data-role="button" data-icon="delete" data-inline="true">Delete</button></div>
</div>
<div class="jqm-demos jqm-demos-index" id="Case" data-role="page">
<div class="jqm-header" data-role="header">
<h1 class="jqm-logo"><a href="#"><img alt="SalesIO" src="{!URLFOR($Resource.SalesIOLogo, '')}" /></a></h1>
 <a class="jqm-navmenu-link" href="#" data-icon="bars" data-iconpos="notext">Navigation</a></div>
<!-- /header -->
<div class="jqm-content" id="caseList" data-role="content"></div>
<!-- /content -->

<div data-role="footer" data-id="foo1" data-position="fixed">
<div data-role="navbar">
<ul>
	<li><a class="ui-btn-active ui-state-persist" id="addcase" data-prefetch="true" data-transition="none" data-icon="grid"></a>New</li>
</ul>
</div>
<!-- /navbar --></div>
<!-- /footer --></div>
<pre>
<!-- /page --></pre>
<div class="jqm-demos jqm-demos-index" id="csdetailpage" data-role="page">
<div class="jqm-header" data-role="header">
<h1 class="jqm-logo"><a href="#"><img alt="SalesIO" src="{!URLFOR($Resource.SalesIOLogo, '')}" /></a></h1>
 <a class="jqm-navmenu-link" href="#" data-icon="bars" data-iconpos="notext">Navigation</a></div>
<!-- /header -->
<div class="jqm-content" data-role="content">
<div data-role="fieldcontain"><label for="csStatus">Status:</label>
 <input id="csStatus" type="text" name="csStatus" /></div>
<div data-role="fieldcontain"><label for="csReason">Reason:</label>
 <input id="csReason" type="text" name="csReason" /></div>
<div data-role="fieldcontain"><label for="csOrigin">Origin:</label>
 <input id="csOrigin" type="text" name="csOrigin" /></div>
<div data-role="fieldcontain"><label for="csSubject">Subject:</label>
 <input id="csSubject" type="text" name="csSubject" /></div>
<div data-role="fieldcontain"><label for="csDescription">Description:</label>
 <input id="csDescription" type="text" name="csDescription" /></div>
<h2 id="error" style="color: red;"></h2>
 <input id="caseId" type="hidden" />

 <button class="save" id="savecase" data-role="button" data-icon="check" data-inline="true" data-theme="b">Save</button>
 <button class="destroy" id="deletecase" data-role="button" data-icon="delete" data-inline="true">Delete</button></div>
</div>
<div class="jqm-demos jqm-demos-index" id="Chatter" data-role="page">
<div class="jqm-header" data-role="header">
<h1 class="jqm-logo"><a href="#"><img alt="SalesIO" src="{!URLFOR($Resource.SalesIOLogo, '')}" /></a></h1>
 <a class="jqm-navmenu-link" href="#" data-icon="bars" data-iconpos="notext">Navigation</a></div>
<!-- /header -->
<div data-role="content">
<div data-role="fieldcontain"><label for="chattertitle">Title:</label>
 <input id="chattertitle" type="text" name="chattertitle" /></div>
<div data-role="fieldcontain"><label for="chatterbody">Body:</label>
 <textarea id="chatterbody" name="chatterbody" rows="5"></textarea></div>
 <input id="cwhoId" type="hidden" name="cwhoId" value="{!userId}" />
<h2 id="error" style="color: red;"></h2>
 <button class="save" id="savechatter" data-role="button" data-icon="check" data-inline="true" data-theme="b">Save</button></div>

<div data-role="footer" data-id="foo1" data-position="fixed">
<div data-role="navbar">
<ul>
	<li><a class="ui-btn-active ui-state-persist" href="#NewChatter" data-prefetch="true" data-transition="none">New</a></li>
	<li><a href="#ViewChatter" data-prefetch="true" data-transition="none">View</a></li>
</ul>
</div>
<!-- /navbar --></div>
<!-- /footer --></div>
<div class="jqm-demos jqm-demos-index" id="Email" data-role="page">
<div class="jqm-header" data-role="header">
<h1 class="jqm-logo"><a href="#"><img alt="SalesIO" src="{!URLFOR($Resource.SalesIOLogo, '')}" /></a></h1>
 <a class="jqm-navmenu-link" href="#" data-icon="bars" data-iconpos="notext">Navigation</a></div>
<!-- /header -->
<div data-role="content">
<div data-role="fieldcontain"><label for="emailAddress">Address:</label>
 <input id="emailAddress" type="text" name="emailAddress" value="{!emailAddress}" /></div>
<div data-role="fieldcontain"><label for="emailSubject">Title:</label>
 <input id="emailSubject" type="text" name="emailSubject" value="{!emailSubject}" /></div>
<div data-role="fieldcontain"><label for="emailBody">Body:</label>
 <textarea id="emailBody" name="emailBody" rows="5"></textarea></div>
<h2 id="error" style="color: red;"></h2>
 <button class="save" id="sendemail" onclick="emailFunction();" data-role="button" data-icon="check" data-inline="true" data-theme="b">Send</button>






</div>
</div>
<div class="jqm-demos jqm-demos-index" id="SMS" data-role="page">
<div class="jqm-header" data-role="header">
<h1 class="jqm-logo"><a href="#"><img alt="SalesIO" src="{!URLFOR($Resource.SalesIOLogo, '')}" /></a></h1>
 <a class="jqm-navmenu-link" href="#" data-icon="bars" data-iconpos="notext">Navigation</a></div>
<!-- /header -->
<div data-role="content">
<div data-role="fieldcontain"><label for="smsNumber">Number:</label>
 <input id="smsNumber" type="text" name="smsNumber" /></div>
<div data-role="fieldcontain"><label for="smsBody">Body:</label>
 <textarea id="smsBody" name="smsBody" rows="5"></textarea></div>
<h2 id="error" style="color: red;"></h2>
 <button class="save" id="sendsms" onclick="smsFunction();" data-role="button" data-icon="check" data-inline="true" data-theme="b">Send</button>



</div>
</div>
<div class="jqm-demos jqm-demos-index" id="Meeting" data-role="page">
<div class="jqm-header" data-role="header">
<h1 class="jqm-logo"><a href="#"><img alt="SalesIO" src="{!URLFOR($Resource.SalesIOLogo, '')}" /></a></h1>
 <a class="jqm-navmenu-link" href="#" data-icon="bars" data-iconpos="notext">Navigation</a></div>
<!-- /header -->
<div data-role="content">
<div data-role="fieldcontain"><label for="esubject">Subject:</label>
 <input id="esubject" type="text" name="esubject" /></div>
<div data-role="fieldcontain"><label for="estartdatetime">Start Date:</label>
 <input id="estartdatetime" type="datetime-local" name="estartdatetime" value="" data-clear-btn="true" /></div>
<div data-role="fieldcontain"><label for="eenddatetime">End Date:</label>
 <input id="eenddatetime" type="datetime-local" name="eenddatetime" value="" data-clear-btn="true" /></div>
<div data-role="fieldcontain"><label for="edescription">Description:</label>
 <textarea id="edescription" name="edescription" rows="5"></textarea></div>
 <input id="ewhoId" type="hidden" name="ewhoId" value="{!userId}" />
<h2 id="error" style="color: red;"></h2>
 <button class="save" id="saveevent" data-role="button" data-icon="check" data-inline="true" data-theme="b">Save</button></div>
</div>
<div class="jqm-demos jqm-demos-index" id="Task" data-role="page">
<div class="jqm-header" data-role="header">
<h1 class="jqm-logo"><a href="#"><img alt="SalesIO" src="{!URLFOR($Resource.SalesIOLogo, '')}" /></a></h1>
 <a class="jqm-navmenu-link" href="#" data-icon="bars" data-iconpos="notext">Navigation</a></div>
<!-- /header -->
<div data-role="content">
<h2 id="error" style="color: red;"></h2>
 <button class="save" id="addtask" data-role="button" data-icon="check" data-inline="true" data-theme="b">Send</button>

 <input id="twhoId" type="hidden" name="twhoId" value="{!userId}" /></div>
</div>
<div class="jqm-demos jqm-demos-index" id="Todo" data-role="page">
<div class="jqm-header" data-role="header">
<h1 class="jqm-logo"><a href="#"><img alt="SalesIO" src="{!URLFOR($Resource.SalesIOLogo, '')}" /></a></h1>
 <a class="jqm-navmenu-link" href="#" data-icon="bars" data-iconpos="notext">Navigation</a></div>
<!-- /header -->
<div data-role="content"><form>
<fieldset data-role="controlgroup"><legend>Vertical:</legend>
 <input id="checkbox-v-2a" type="checkbox" name="checkbox-v-2a" />
 <label for="checkbox-v-2a">One</label>
 <input id="checkbox-v-2b" type="checkbox" name="checkbox-v-2b" />
 <label for="checkbox-v-2b">Two</label>
 <input id="checkbox-v-2c" type="checkbox" name="checkbox-v-2c" />
 <label for="checkbox-v-2c">Three</label></fieldset>
<h2 id="error" style="color: red;"></h2>
 <button class="save" id="addtodo" data-role="button" data-icon="check" data-inline="true" data-theme="b">Save</button></form></div>
</div>
<div class="jqm-demos jqm-demos-index" id="Notes" data-role="page">
<div class="jqm-header" data-role="header">
<h1 class="jqm-logo"><a href="#"><img alt="SalesIO" src="{!URLFOR($Resource.SalesIOLogo, '')}" /></a></h1>
 <a class="jqm-navmenu-link" href="#" data-icon="bars" data-iconpos="notext">Navigation</a></div>
<!-- /header -->
<div data-role="content"><form>
<div class="ui-hide-label" data-role="fieldcontain"><textarea id="textarea-16" style="height: 300px;" name="textarea-16"></textarea></div>
<h2 id="error" style="color: red;"></h2>
 <button class="save" id="addnotes" data-role="button" data-icon="check" data-inline="true" data-theme="b">SAVE</button></form></div>
</div>
<div class="jqm-demos jqm-demos-index" id="Charts" data-role="page">
<div class="jqm-header" data-role="header">
<h1 class="jqm-logo"><a href="#"><img alt="SalesIO" src="{!URLFOR($Resource.SalesIOLogo, '')}" /></a></h1>
 <a class="jqm-navmenu-link" href="#" data-icon="bars" data-iconpos="notext">Navigation</a></div>
<!-- /header -->
<div data-role="content"></div>
</div>
<pre>

</pre>
<div id="modal" data-role="page">
<div data-role="header" data-theme="e">
<h1>Dialog (modal)</h1>
</div>
<!-- /header -->
<div data-role="content" data-theme="d">
<h2>Modal</h2>
Content of Modal Goes Here</div>
<!-- /content -->
<div data-role="footer">
<h4>Page Footer</h4>
</div>
<!-- /footer --></div>
<pre>
<!-- /page modal -->

<script type="text/javascript">// <![CDATA[
      new Morris.Line({
  // ID of the element in which to draw the chart.
  element: 'myfirstchart',
  // Chart data records -- each entry in this array corresponds to a point on
  // the chart.
  data: [
    { year: '2008', value: 20 },
    { year: '2009', value: 10 },
    { year: '2010', value: 5 },
    { year: '2011', value: 5 },
    { year: '2012', value: 20 }
  ],
  // The name of the data record attribute that contains x-values.
  xkey: 'year',
  // A list of names of data record attributes that contain y-values.
  ykeys: ['value'],
  // Labels for the ykeys -- will be displayed when you hover over the
  // chart.
  labels: ['Value']
});

// ]]></script>


Clone on Github

git clone https://github.com/thysmichels/Sales-IO-JQuery-Mobile-Force.com-Mobile-App.git

Force.com Apex and Visualforce Best Practices

Triggers

  • There should only be one trigger for each object.
Apex Trigger Code

trigger ContactTrigger on Contact (after delete, after insert, after undelete, 
after update, before delete, before insert, before update) 
{
        ContactTriggerHandler handler = new ContactTriggerHandler(Trigger.isExecuting, Trigger.size);

        if(Trigger.isInsert &amp;&amp; Trigger.isBefore)
        {
		handler.OnBeforeInsert(Trigger.new);
	}
	else if(Trigger.isInsert &amp;&amp; Trigger.isAfter){
		handler.OnAfterInsert(Trigger.new);
		ContactTriggerHandler.OnAfterInsertAsync(Trigger.newMap.keySet());
	}

	else if(Trigger.isUpdate &amp;&amp; Trigger.isBefore){
		handler.OnBeforeUpdate(Trigger.old, Trigger.new, Trigger.newMap);
	}
	else if(Trigger.isUpdate &amp;&amp; Trigger.isAfter){
		handler.OnAfterUpdate(Trigger.old, Trigger.new, Trigger.newMap);
		ContactTriggerHandler.OnAfterUpdateAsync(Trigger.newMap.keySet());
	}

	else if(Trigger.isDelete &amp;&amp; Trigger.isBefore){
		handler.OnBeforeDelete(Trigger.old, Trigger.oldMap);
	}
	else if(Trigger.isDelete &amp;&amp; Trigger.isAfter){
		handler.OnAfterDelete(Trigger.old, Trigger.oldMap);
		ContactTriggerHandler.OnAfterDeleteAsync(Trigger.oldMap.keySet());
	}

	else if(Trigger.isUnDelete){
		handler.OnUndelete(Trigger.new);	
	}
}

Apex Class Code

public with sharing class ContactTriggerHandler {
	private boolean isHandlerExecuting = false;
	private integer BatchSize = 0;

	public ContactTriggerHandler(boolean isExecuting, integer size){
		isHandlerExecuting = isExecuting;
		BatchSize = size;
	}

	public void OnBeforeInsert(Contact[] newContacts){
		//Example usage
		for(Contact newContact : newContacts){
			if(newContact.Name == null){
				newContact.Name.addError('Missing Name Field');
			}
		}
	}

	public void OnAfterInsert(Contact[] newContacts){

	}

	@future public static void OnAfterInsertAsync(Set newContactIDs){
		//Example usage
		List newContacts = [select Id, Name from Contact where Id IN :newContactIDs];
	}

	public void OnBeforeUpdate(Contact[] oldContact, Contact[] updatedContacts, Map contactMap){
		//Example Map usage
		Map contacts = new Map( [select Id, FirstName, LastName, Email from Contact where Id IN :contactMap.keySet()] );
	}

	public void OnAfterUpdate(Contact[] oldContacts, Contact[] updatedContacts, Map contactMap){

	}

	@future public static void OnAfterUpdateAsync(Set updatedContactIDs){
		List updatedContacts = [select Id, Name from Contact where Id IN :updatedContactIDs];
	}

	public void OnBeforeDelete(Contact[] contactsToDelete, Map contactMap){

	}

	public void OnAfterDelete(Contact[] deletedContacts, Map contactMap){

	}

	@future public static void OnAfterDeleteAsync(Set deletedContactIDs){

	}

	public void OnUndelete(Contact[] restoredContacts){

	}

	public boolean IsTriggerContext{
		get{ return isHandlerExecuting;}
	}

	public boolean IsVisualforcePageContext{
		get{ return !IsTriggerContext;}
	}

	public boolean IsWebServiceContext{
		get{ return !IsTriggerContext;}
	}

	public boolean IsExecuteAnonymousContext{
		get{ return !IsTriggerContext;}
	}
}

  • Use Helper Classes


https://github.com/thysmichels/Force.com-Helper-Classes
https://github.com/thysmichels/base
http://code.google.com/p/apex-lang/

Dynamic Delete Batch Apex Class
global class dynamicDelete implements Database.Batchable{

   global final String Query;

   global dynamicDelete(String q)
   {
      Query=q;
   }

   global Database.QueryLocator start(Database.BatchableContext BC)
   {
      return Database.getQueryLocator(query);
   }

   global void execute(Database.BatchableContext BC, List scope)
   {
      delete scope;
   }
}

Scheduler for Batch Apex Delete

global class dynamicDeleteScheduler implements Schedulable{
   private static final String deleteLeads = 'Select id from Lead where Email like \'%test%\' limit 5000';
   private static final String deleteTasks = 'Select id from Task where WhoId IN (Select id from Contact where email like \'%test%\') limit 50000';
   private static final String deleteOpportunities = 'Select id from Opportunity where id in (Select OpportunityId from OpportunityContactRole where Contact.Email like \'%test%\') limit 50000';
   private static final String deleteCases = 'Select id from Case where ContactId IN (Select id from Contact where Email like \'%test%\') limit 50000';
   private static final String deleteTestAccounts = 'Select id from Account where Email__c like \'%test%\' limit 50000';
   private static final String deleteTestContacts = 'Select id from Contact where Email like \'%test%\' limit 50000';

   global void execute(SchedulableContext SC) 
   {     
      dynamicDelete deleteTask = new dynamicDelete(deleteTasks);
      Database.executebatch(deleteTask);

      dynamicDelete deleteCase = new dynamicDelete(deleteCases);
      Database.executebatch(deleteCase);

      dynamicDelete deleteOpportunity = new dynamicDelete(deleteOpportunities);
      Database.executebatch(deleteOpportunity);

      dynamicDelete deletecontact = new dynamicDelete(deleteTestContacts);
      Database.executebatch(deletecontact);

      dynamicDelete deleteaccount = new dynamicDelete(deleteTestAccounts); 
      Database.executebatch(deleteaccount);

   }
}
    Bulkify Triggers to process up to 200 records for each call. This will help you avoid hitting DML limit per process.
Bulk your DML operations with with List/Maps/Sets
private void bulkUpdateExample(Map updateContactOwner)
{
    List&lt;Contact&gt; listToHoldListOfContactsToBeUpdated = new List&lt;Contact&gt;();
    Id salesForceAdminId = [Select id from User where Name='Salesforce Administrator'].Id;
    for (Id contactIdToUpdateOwner : updateContactOwner.keySet())
    {
          updateContactOwner.get(contactIdToUpdateOwner).OwnerId = salesForceAdminId;
          //Add all records to a List/Map/Set to be updated outside the for loop
          listToHoldListOfContactsToBeUpdated.add(updateContactOwner.get(contactIdToUpdateOwner));
    }
    //Always update outside a for loop
    update listToHoldListOfContactsToBeUpdated;
}

Bulkify your Test classes with at least 200 records

@isTest(SeeAllData=false) static void testContactOwnerReassignmentSuccess()
{
  Account createAccount = new Account(Name='[TEST]');
  insert createAccount;

  List contactList = new List();
  for (int k = 0; k &lt; 200; k++)
      contactList.add(new Contact(FirstName='[Test]' + k, AccountId=createAccount.Id));

  Test.startTest();
  insert contactList;
  Test.stopTest();

  System.assert(200 == [Select count() from Contact where 
Owner.Name = 'Salesforce Administrator']);
}
    Use Collections in SOQL “WHERE” clauses to retrieve all records using a single query. Also use self, semi and anti joins:
 legalDocumentNames = new String[]
         {
            'Employment Application',
            'Release of Liability',
            'Non-Disclosure Agreement',
            '%Employment Agreement'
          }
List legalDocumentQuery = [select ContactId from Asset
where Product2.Name Like :legalDocumentNames];

Apex 

    Use @future annotation for methods that can be executed asynchronously
@future public static void OnAfterInsertAsync(Set newAssetIDs)
{
    new smbNurtureCampaign().insertCampaignMemebersToControlAndTestCampaign(newAssetIDs);
}
    Provide proper exception handling.
Apply try catch to all methods
public static User getSalesRepsForRouting(List getListActiveUser)
    {
        try
        {
          return getListActiveUser.size() == 1 ? getListActiveUser[0] : getListActiveUser[0];
        }
        catch(handlerException e)
        {
            Debugger.createDebugRecord(e, 'ACRoundRobinV5');
            sendErrorMail(e.getMessage());
            return null;
        }
    }    

Extend the Exception Class

      public class handlerException extends Exception{}

Catch Exception and create record in custom Debugger Object

public static void createDebugRecord(handlerException exceptionObject, String methodName)
     {
         Database.insert(new Debugger__c
            (   
                    Message__c = exceptionObject.getMessage(),
                    Line_Number__c = exceptionObject.getLineNumber(),
                    Stack_Trace__c = exceptionObject.getStackTraceString(),
                    Method_Name__c=methodName
            ), false);
     }
     public static void createDebugRecord(DMLException dmlexceptionObject, String methodName)
     {
         Database.insert(new Debugger__c
            (       
                    DML_Record_Id__c = dmlexceptionObject.getDmlId(0),
                    DML_Type__c = dmlexceptionObject.getTypeName(),
                    Message__c = dmlexceptionObject.getDmlMessage(0),
                    Line_Number__c = dmlexceptionObject.getDmlIndex(0),
                    Stack_Trace__c = dmlexceptionObject.getDmlStatusCode(0),
                    Method_Name__c=methodName
            ),false);
     }
    Prevent SOQL and SOSL injection attacks by using static queries, binding variables or the escapeSingleQuotes method.
        private static final String[] QUERY_SELECT_STATEMENT =
        new String[]{'select ',' from ', ' where Id = \''};

        String query = QUERY_SELECT_STATEMENT[0];
        for(Schema.DescribeFieldResult dfr : queryFields)
            query = query + dfr.getName() + ',';

        query = query.subString(0,query.length() - 1);
        query = query + QUERY_SELECT_STATEMENT[1];
        query = query + obj.getName();
        query = query + QUERY_SELECT_STATEMENT[2];
        query = query + theId + '\'';
    When querying large data sets, use a SOQL “for” loop
        for (Account accountToCheckAndUpdateAccountDetails : [Select BillingStreet, BillingCity, BillingState, BillingPostalCode, BillingCountry, ShippingStreet, ShippingCity, ShippingState, ShippingPostalCode, ShippingCountry, Phone, Fax, Website, Industry from Account])
        {
           .....
        }
        update accountToCheckAndUpdateAccountDetails;
    Use Apex Limits Methods to avoid hitting governor exceptions.
In the if statement we check if it is save to proceed without hitting governor limits 
List accountOwnerDetail = [SELECT OwnerId, Owner.Name, Owner.Profile.Name, Owner.isActive from Account];
if (accountOwnerDetail.size() + Limits.getDMLRows() &lt; Limits.getLimitDMLRows())
{
  ... 
}
    No SOQL or SOSL queries inside loops
Create parent-to-child and child-to-parent relationship queries outside for loops
Contact getContactWithAccount = Database.query('Select Name, Email,  Account.Name, Account.BillingStreet from Contact limit 1');
Account getAccountContactOpportunityInOneQuery = Database.query('Select Id, (Select Name from Account.Contacts), (Select Name from Account.Opportunities) from Account limit 1');

Tip: Update an Object without querying for it

List accountFieldsToBeUpdated = new List();
for (Contact updateAccountsFieldFromContact : [Select AccountId from Contact where OtherCity ='San Francisco'])
{ 
    //create new account object with the accountId from contact
     Account newAccountObject = new Account(Id=updateAccountsFieldFromContact.AccountId);
     newAccountObject.ShippingCity = 'SF';
     newAccountObject.BillingCity = 'SF';   
     accountFieldsToBeUpdated.add(newAccountObject);
}
update accountFieldsToBeUpdated ;
    Do not use hardcoded IDs: As ID's are unique and different between environments.
Don't use ID's rather get Id's by Name
        Opportunity getOpportunity = [Select Name from Opportunity where Owner.Id='00560000001S1eh' limit 1];

Rather do the following as your unit tests will fail in other environments

        Opportunity getOpportunity = [Select Name from Opportunity where Owner.Name='John Black' limit 1];

Visualforce

    Do not hardcode picklists in Visualforce pages; include them in the controller instead.
Picklist Controller Method
public List getFilterByRecordTypeList() 
    {
            List&lt;SelectOption&gt; op = new List&lt;SelectOption&gt;();
            Set&lt;Id&gt; setPPFamily = new Set&lt;Id&gt;();
            setPPFamily.clear();
            setPPFamily.add('All Opportunities');
            List&lt;AggregateResult&gt; aggregateResultsOfMotivatingProductFamily = [SELECT RecordType.Name FROM Opportunity Group By RecordType.Name];

            for (AggregateResult ppfamilyname : aggregateResultsOfMotivatingProductFamily)
            op.add(new SelectOption((String)ppfamilyname.get('RecordType.Name'),(String)ppfamilyname.get('RecordType.Name')));

            op.sort();
            return op;
    }

Calling Picklist Controller in Visualforce

&lt;apex:outputText style="color:#f8f8ff;font-weight:bold" value="Filter By Document Type"/&gt;
&lt;apex:toolbarGroup itemSeparator="line" location="left" id="toobarGroupForm2"&gt;
&lt;apex:selectList label="Filter by Record Type" value="{!FilterByRecordType}" size="1" required="false" &gt;
&lt;apex:actionSupport event="onchange" action="{!FilterByRecordTypeAction}" status="filterByRecordType" reRender="innerblock"/&gt;
&lt;apex:outputPanel style="color:#f8f8ff;font-weight:bold"&gt;
&lt;apex:actionStatus id="filterByRecordType" startText="Filtering your records..." stopText=""/&gt;
&lt;/apex:outputPanel&gt;
&lt;apex:selectOptions value="{!FilterByRecordTypeList}"/&gt;
&lt;/apex:selectList&gt;
&lt;/apex:toolbarGroup&gt;
    Javascript and CSS should be included as Static Resources allowing the browser to cache them.
Import your JS and CSS to the Static Resources as Zip of single files. Include CSS on top and JS at the bottom for faster loading
&lt;apex:page controller="YourCustomController" sidebar="true" cache="true" showChat="true" &gt;
&lt;head&gt;
&lt;apex:stylesheet value="{!URLFOR($Resource.jQuery1, '/css/ui-lightness/jquery-ui-1.8.23.custom.css')}" /&gt;
&lt;/head&gt;
&lt;body&gt;
....

<apex:includeScript value="{!URLFOR($Resource.jQuery1, '/js/jquery-1.8.0.min.js')}" />
<apex:includeScript value="{!URLFOR($Resource.jQuery1, '/js/jquery-ui-1.8.23.custom.min.js')}" />
</body>
</apex:page>
 

    Mark controller variables as “transient” if they are not needed between server calls. This will make your page load faster as it reduces the size of the View State.
Transient Example Visualforce Page
&lt;apex:page controller="ExampleController"&gt;
&lt;apex:form id="theForm"&gt;
&lt;apex:panelBar id="thePanel"&gt;
&lt;apex:panelbarItem label="Transient vs Non Transient"&gt;
&lt;b&gt;Transient Time: &lt;/b&gt; {!t1} &lt;br /&gt;
&lt;b&gt;Non Transient Time: &lt;/b&gt; {!t2}
&lt;/apex:panelbarItem&gt;
&lt;apex:actionPoller interval="5" enabled="true" rerender="thePanel" action="{!refresh}" /&gt;
&lt;/apex:panelBar&gt;
&lt;/apex:form&gt;
&lt;/apex:page&gt;

Transient Example Controller

public class ExampleController {

    public PageReference refresh() {
        return null;
    }

    DateTime t1;
    transient DateTime t2;

    public String getT1() {
        if (t1 == null) t1 = System.now();
        return '' + t1;
    }

    public String getT2() {
        if (t2 == null) t2 = System.now();
        return '' + t2;
    }
}
    Use the <repeat> tag to iterate over large collections.
&lt;apex:page standardController="Opportunity" id="thePage"&gt;
&lt;table id="EquipmentTable" border="1"&gt;
&lt;apex:repeat value="{!Opportunity}" var="line" id="theRepeat"&gt;
&lt;tr&gt;
&lt;td valign="top"&gt;&lt;apex:outputField value="{!line.Id}" /&gt;&lt;/td&gt;
&lt;td valign="top"&gt;&lt;apex:outputField value="{!line.Name}" /&gt;&lt;/td&gt;
&lt;td valign="top"&gt;&lt;apex:outputField value="{!line.Probability}" /&gt;&lt;/td&gt;
&lt;td valign="top"&gt;&lt;apex:outputField value="{!line.Amount}" /&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/apex:repeat&gt;
&lt;/table&gt;
&lt;/apex:page&gt;

Dynamic SOQL Queries packages and security

For managed packaged development dynamic SOQL queries assist developers with creating soql queries on the fly without hard coding field names which may not be there in all orgs. When you install an managed package you may run into trouble is some fields do not exist. This is why dynamic SOQL queries can be beneficial by using the schema describe objects. The code below can help any developer to retrieve the fields from any object by providing the object name. From that you can build a dynamic soql query and also define the data type for each field. Be careful when using Dynamic SOQL queries as it can be prone to SOQL injection attacks. Make sure your return type is static and final.

Return the Schema.SObjectType for the specific Object’s ID we provide

public static Schema.SObjectType getObjectSchema(String objectSObjectName)
    {
        if (sobjectSchemaMap.isEmpty())
        	getSchemaMap();

        return sobjectSchemaMap.get(objectSObjectName);
    }

SchemaMap() method to return the Schema Map for your organization.

private static Map<String, Schema.SObjectType> sobjectSchemaMap;
public static Map getSchemaMap()
    {
        return sobjectSchemaMap == null ? Schema.getGlobalDescribe() : sobjectSchemaMap;
    }

Dynamically creating the SOQL Query when we have the queryFields, Object and Id.

private String query;
public String buildQueryAllString(List queryFields,DescribeSObjectResult obj, String theId)
    {
        query = QUERY_SELECT_STATEMENT[0];
        for(Schema.DescribeFieldResult dfr : queryFields)
            query = query + dfr.getName() + ',';

        query = query.subString(0,query.length() - 1);
        query = query + QUERY_SELECT_STATEMENT[1];
        query = query + obj.getName();
        query = query + QUERY_SELECT_STATEMENT[2];
        query = query + theId + '\'';
        return query;
    }

Putting it all together:
1. We provide and Record ID.
2. find the SObject for the ID provided.
3. Get the SObject Fields for the SObject
4. Create a SOQL Query
5. Run the SOQL Query to retrieve information.

public SObject processSchemaInfo(String id)
    {
    	try
    	{
        schemaMap = DynamicSOQL.getSchemaMap();
        sobjects = schemaMap.values();

        Schema.DescribeSObjectResult objDescribe;
        List tempFields;

        buildQuery = '';
        fields = new List();

        for(Schema.SObjectType objType : sobjects)
        {
            objDescribe = objType.getDescribe(); 
            String sobjectPrefix = objDescribe.getKeyPrefix();
            if(id != null && sobjectPrefix != null && id.startsWith(sobjectPrefix))
            {
                objectType = objDescribe.getLocalName();
                Map<String, Schema.SObjectField> fieldMap = objDescribe.fields.getMap();
                tempFields = fieldMap.values();
                for(Schema.SObjectField sof : tempFields)
                {
                    fields.add(sof.getDescribe());
                }
                buildQuery = buildQueryAllString(fields,objDescribe,id);
            }
        }

        return  Database.query(buildQuery);
       }
       catch(Exception ex)
       {
       		System.debug(ex.getMessage());
       		return null;
       }
    }

Minimize and Optimize Apex Test Classes with SmartFactory

I was writing allot of test code to create SObjects and all the necessary fields. There are some problems when writing test code when specifying all the necessary fields.

Some of the things to consider when creating test fields is required fields and pre-populating lookups. Let us look at some examples:
Let’s start of creating a Account in a Test Class:

Account newAccount = new Account(Name='This is a Test Account');

Now we create the Contact associated to that Account:

Contact newContact = new Contact(AccountId=newAccount.Id);

but Contact has more required fields and will fail if I call the:

 insert newContact;

Let’s take it a bit further and create an Opportunity and select the newAccount.

Opportunity newOpportunity = new Opportunity(AccountId=newAccount.Id);

the same story for opportunity is we need more required fields to be completed, and do we always know the required fields?No for some unknown Salesforce orgs this may be a problem.

Let’s see how we can solve the problem of required fields and lookup during sObject creation:

Contact c = (Contact)SmartFactory.createSObject('Contact', true);

The second argument (true) toggles the cascade option, which populates any lookup fields on the object with their own newly created objects. It’s a powerful way to create hierarchies of test data quickly and easily so you can focus on writing test and implementation logic.

Now we can create an Opportunity without specifying an Account or Contact:

Opportunity o = (Opportunity)SmartFactory.createSObject('Opportunity', true);

This will create, a Account, Contact and Opportunity for you in one line.

SmartFactory uses the describe metadata to populate all fields with data of the right type. It currently handles string and lookup fields. For lookup fields, it creates an appropriate object and then uses that object’s ID.

Force.com IDE MavensMate for the serious Apex developer

You are frustrated with coding Apex in Eclipse because it is slow or you are coding in the browser😦 not the best thing when you close the window and loose all your code…to the rescue MavensMate. MavensMate is an Force.com IDE is an add-on project running on Sublime Text 2. I have been using MavensMate for a while now and have seen a great benefit in time, auto completion (less time typing) and just easier all in one experience from coding, testing to deployment.

The following functionality is awesome:
1. Code completion
2. Quick code search
3. When code is saved, validation takes a few seconds (compared to Eclipse it is faster)
4. Testing code inside MavensMate is a breeze and you can play games while waiting for it to complete.
5. Direct integration to your Git repository.
6. Create new files (Apex or Visualforce) from inside a Project
7. Easy to run Anonymous Apex code.
8. Easy to deploy code to different SFDC environments.
9. Manage your metadata between environments.
10. Great documentation to explain how to use MavensMate.

I recommend MavensMate to all serious Apex developers. Follow these steps below to get started:
https://github.com/joeferraro/MavensMate-SublimeText

Query SObject without burning SOQL queries

When you are writing Apex code you want to minimize the amount of SOQL queries in your code. One of the ways todo this is to create an SObject with some information of that object.

Let us say you have the following SOQL query:

Contact getAccountId = [Select AccountId from Contact limit 1];

Now you would like more information about the Account for that contact, you where thinking of taking the AccountId and running another SOQL query like this:

Account showAccountInfo = [Select Id from Account where Id=:getAccountId.AccountId];

You are burning unnecessary SOQL queries. You can do this following:

 Account showAccountInfo = new Account(Id=:getAccountId.AccountId);

You are not burning a SOQL query as you now have created an Object of that account. So you can do the following to query or update fields:

Get Information:

showAccountInfo.Name

Assign New Value:

showAccountInfo.Name='New Account Name'
update showAccountInfo;

Salesforce Update Product Schedule from Opportunity Product using Apex Trigger

Update the Product ScheduleDate when the Opportunity Product ServiceDate changes. First we create an OpportunityProduct Trigger to see when ServiceDate changes and parse the amount of days difference between the old and new date.

trigger ProductDateUpdate on OpportunityLineItem (after update) {
   
    for (OpportunityLineItem oli: Trigger.new) {
        OpportunityLineItem oldCloseDate = Trigger.oldMap.get(oli.ID);
        if (oli.ServiceDate != oldCloseDate.ServiceDate) {
            Integer dateval = oldCloseDate.ServiceDate.daysBetween(oli.ServiceDate);
            ProductScheduleUpdate.ScheduleProductUpdate(oli.id, dateval);  
        }
    }
}

The Trigger class then updates all the Product Schedule Dates by the amount of days.

public class ProductScheduleUpdate
{
    public static void ScheduleProductUpdate(String oid, Integer dateval)
    {
           List datelist;
           datelist = [SELECT ScheduleDate FROM OpportunityLineItemSchedule WHERE OpportunityLineItemId =:oid];
           for (Integer k = 0; k < datelist.size(); k++)
               {
               System.debug('DateList = ' + datelist[k]);
               Date mydate=Date.newInstance(datelist[k].ScheduleDate.Year(),datelist[k].ScheduleDate.Month(),datelist[k].ScheduleDate.Day());
               mydate=mydate.addDays(dateval);
               datelist[k].ScheduleDate = mydate;
               update datelist;
               //date datediff = date.parse(datelist[k]);
               }
        
    }
}
%d bloggers like this: