Showing posts with label APEX Code. Show all posts
Showing posts with label APEX Code. Show all posts

Tuesday, January 6, 2015

Simple Visualforce Page to Allow Updates to Related List

I have had many requests in the past to allow for quick easy updates to a related list. You can use list views to do some of the updates, but there is no way to do a quick update to a related list aside from installing an app. GridBuddy is a great app that can accomplish this at a very reasonable price.

However I recently had a request to do one simple update on Quotes that are related to an Opportunity, just one checkbox and everything else would be read only. Below is the source code for the Visualforce Page, Custom Controller and Test Class.

There are a couple custom fields mixed in here but most of this is standard Salesforce. This would be a Visualforce page that would be placed on the Opportunity page layout and it would show related Quotes with the option to update and save changes.

VF Page:

<apex:page standardController="Opportunity" extensions="quoteController">
 
 <apex:outputPanel id="refresh" rendered="true">
 <apex:outputPanel id="refresh1" rendered="{!refreshPage}">
  <script>
   window.top.location='/{!Opportunity.id}';
  </script>
 </apex:outputPanel>
</apex:outputPanel>

 <style>
.pbBody td {
 border-color: #E3DEB8;
 border-style: none none solid;
 border-width: medium medium 2px;
 padding-bottom: 4px;
 padding-top: 4px;
 width: 85px;
}
.pbBody input   { width: 105px;}
.pbBody .nameColumn { width: 125px;}
.hdr     {;}
</style>
<apex:form >


 <apex:messages />

 <apex:pageBlock title="Edit Quotes">
  <apex:pageBlockButtons >
   <apex:commandButton action="{!saveChanges}" value="Save" status="ajaxStatus" />
  </apex:pageBlockButtons>
  <apex:actionStatus id="ajaxStatus" startText="Updating quotes...">
   <apex:facet name="stop">

   <apex:outputPanel id="main"> 
       
       
    <table>
    <tr>
     <apex:repeat value="{!headers}" var="h">
      <td class="hdr">{!h}</td>
     </apex:repeat>
    </tr>

    <apex:repeat value="{!Quotes}" var="q">
     <tr>
      <td><apex:outputField value="{!q.QuoteNumber}" /></td>
      <td><apex:outputField value="{!q.Name}" /></td>
      <td><apex:outputField value="{!q.IsSyncing}" /></td>
      <td><apex:inputField value="{!q.Price__c}" /></td>
      <td><apex:inputField value="{!q.Margin__c}" /></td>  
     </tr>
    </apex:repeat>
    </table>  
   </apex:outputPanel>
   </apex:facet>
  </apex:actionStatus>
 </apex:pageBlock>
</apex:form>
</apex:page>


Custom Apex Controller:

public class quoteController {

public Boolean refreshPage {get; set;}
  
  // Constructor
 public quoteController (ApexPages.StandardController controller) {
  
  refreshPage=false;
  
  this.opp = (Opportunity)controller.getSubject();
  
     this.quote = [ SELECT 
      q.Id, q.QuoteNumber, q.name, q.IsSyncing, q.Price__c, 
      q.Margin__c
      FROM 
      Quote q 
      WHERE 
      q.OpportunityID = :opp.id ];
 }


 // Action Method called from page button
 public pagereference saveChanges() {   
  
  update this.quote;
  refreshPage=true;
  return null;
  
 }


 // Action Method called from page link
 public pagereference newQuote() { 
  Quote q = new Quote();
  q.OpportunityID =this.opp.id; 
  quote.add( q );
  return null;
 }

 // public Getter to provide table headers 
 public string[] getheaders() { return new string [] 
  {'Quote Number','Quote Name','Synching','Price',
  'Margin','Total Margin' } ; }

 // public Getter to list project deliverables
 public Quote[] getQuotes() { 
  return this.quote; 
 } 

 // class variables
 Opportunity opp;
 Quote[] quote; 
}


Test Class:

@isTest
private class controllerTest {
    
    
     public static testMethod void testMyController() {
        
        Account a = new Account(name='Test Account');      
        insert a;

        Opportunity opp = new Opportunity();
        opp.Name = 'Test Opp';
        opp.Amount = 100;
        opp.CloseDate = Date.today();
        opp.Probability = 10;
        opp.StageName = 'New';      
        insert opp;
        
        // start the test execution context
        Test.startTest();

        // set the test's page to your VF page (or pass in a PageReference)
        Test.setCurrentPage(Page.OpportunityQuoteGrid);
        
        // call the constructor
        quoteController controller = new quoteController(new ApexPages.StandardController(opp));
        //quoteController controller = new quoteController();
        //String nextPage = controller.save().getUrl();
        
        // test action methods on your controller and verify the output with assertions
        controller.saveChanges();
        controller.getHeaders();
        controller.newQuote();
        controller.getQuotes();

        
        // stop the test
        Test.stopTest();
        
     }
    
    
}

Monday, October 21, 2013

Apex Trigger - Only Run at Point of Entry

You can put in a very simple condition that will prevent a trigger from running when bulk updates are made. This will only let the trigger run when one record is being edited at a time. This will exclude List View updates and data loader insert/updates:

if (Trigger.new.size() == 1) {

if (Trigger.old[0].isConverted == false){

}

}

Tuesday, July 23, 2013

Using Apex Variables in SOQL and SOSL Queries (SOQL IN Clause)

I spent some time looking for this documentation around using the SOQL IN clause with a list and wanted to save it.

I basically want to run a SOQL query that is looking at a list of String values in the WHERE condition, like this:

// An IN-bind with an Id list. Note that a list of sObjects
// can also be used--the Ids of the objects are used for 
// the bind
Contact[] cc = [SELECT Id FROM Contact LIMIT 2];
Task[] tt = [SELECT Id FROM Task WHERE WhoId IN :cc];

// An IN-bind with a String list
String[] ss = new String[]{'a', 'b'};
Account[] aa = [SELECT Id FROM Account 
                WHERE AccountNumber IN :ss];


http://www.salesforce.com/us/developer/docs/apexcode/Content/langCon_apex_SOQL_variables.htm

Sunday, July 21, 2013

Basic For Loop

Below is a basic example of a for loop. I always seem to need to need this and forget the exact context.


trigger OpportunitySubmitForApproval on Opportunity (after update) {
 
 for (Integer i = 0; i < Trigger.new.size(); i++) {
 
  if (Trigger.old[i].Probability < 30 && Trigger.new[i].Probability >= 30) {
 
   // create the new approval request to submit
   Approval.ProcessSubmitRequest req = new Approval.ProcessSubmitRequest();
   req.setComments('Submitted for approval. Please approve.');
   req.setObjectId(Trigger.new[i].Id);
   // submit the approval request for processing
   Approval.ProcessResult result = Approval.process(req);
   // display if the reqeust was successful
   System.debug('Submitted for approval successfully: '+result.isSuccess());
 
  }
 
 }
 
}

Monday, May 13, 2013

How to Quickly Check the Old Value in an APEX Trigger

Using the Old Map context variable in a trigger can quickly look back at the old value of a field in a trigger. This can be helpful when you need to write a trigger that is checking to see if a value has been changed...

for ( lead l_new: Trigger.new ){

     if ( l_new.Status != Trigger.oldMap.get ( l_new.id ).Status ) {
   
     }

}



Friday, May 10, 2013

Automating Salesforce Approval Processes with Apex Triggers

Ran across this the other day and wanted to re-post it, looks easy enough to implement...

http://blog.jeffdouglas.com/2010/01/04/automating-salesforce-approval-processes-with-apex-triggers/



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
trigger OpportunitySubmitForApproval on Opportunity (after update) {
 
 for (Integer i = 0; i < Trigger.new.size(); i++) {
 
  if (Trigger.old[i].Probability < 30 && Trigger.new[i].Probability >= 30) {
 
   // create the new approval request to submit
   Approval.ProcessSubmitRequest req = new Approval.ProcessSubmitRequest();
   req.setComments('Submitted for approval. Please approve.');
   req.setObjectId(Trigger.new[i].Id);
   // submit the approval request for processing
   Approval.ProcessResult result = Approval.process(req);
   // display if the reqeust was successful
   System.debug('Submitted for approval successfully: '+result.isSuccess());
 
  }
 
 }
 
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@isTest
private class TestOpportunitySubmitForApproval {
 
    static testMethod void testApprovalSuccess() {
 
        Opportunity opp = new Opportunity();
        opp.Name = 'Test Opp';
        opp.Amount = 100;
        opp.CloseDate = Date.today();
        opp.Probability = 10;
        opp.StageName = 'Prospecting';
        // insert the new opp
        insert opp;
        // change the probability of the opp so the trigger submits it for approval
 opp.Probability = 40;
 // update the opp which should submit it for approval
 update opp;
 
        // ensure that the opp was submitted for approval
        List<ProcessInstance> processInstances = [select Id, Status from ProcessInstance where TargetObjectId = :opp.id];
 System.assertEquals(processInstances.size(),1);
 
    }
 
}

Tuesday, March 5, 2013

Try Catch Statement Used When Inserting Records in APEX


try{
    insert pcList;
}
catch(DmlException e){
     System.debug('AgencyCommissionTrigger: DML Exception: Error during AgencyCommissionTrigger updates in the AgencyCommissionTrigger trigger.');
     for (Integer i = 0; i < e.getNumDml(); i++) {
          System.debug(e.getDmlMessage(i));
     }//end for
}//end catch

Monday, February 4, 2013

Pull Record Type ID in Apex


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

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

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

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

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

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

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

}

Friday, February 1, 2013

Map Described

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

Typically you will see it initiated in this way:

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

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




trigger CaseBeforeInsertTrigger on Case (before insert) {

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

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

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

Thursday, January 24, 2013

Adding an Activity to a Chatter Feed Using a Trigger

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

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


Below is a copy of the code Kevin posted:

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

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

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

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

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

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

            feedItems.add(fitem);
        }
    }

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


And here is the test class associated

public with sharing class ChatterUnitTests {

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

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

        insert t;

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







Wednesday, January 16, 2013

Bulkify Apex Triggers

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

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

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

Below is an example of this in action:

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

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

Friday, December 21, 2012

Delete Triggers and Classes from Production in Salesforce using Eclipse IDE

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

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

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

Wednesday, April 25, 2012

Truncating Text In a Trigger Using APEX


Recently I was asked to put a trigger together that copied a Long Text Area value that can hold up to 32,000 characters to a Text Area field that can only hold up to 255 characters. Naturally when you try to copy a value larger than 255 characters to a Text Area field an error will be thrown. So I had to figure out how to basically truncate a string value to 255 characters in order to assign it to a Text Area field.

The string method 'substring' does the trick! What is does is much like in excel using the left(), mid() and right() formulas you give it a starting place and tell it how many characters to grab. Salesforce's basic example is as follow:


'hamburger'.substring(4, 8); 
// Returns "urge" 
    

'smiles'.substring(1, 5);
// Returns "mile" 


My problem was a bit more complex as I had to include logic that would say only perform this truncation when the Long Text Area value is larger than 255 characters. If you try to truncate a string value using substring to a length longer than the string itself you will get an error. Here is the code I used for this instance.



   string EX1 = Sample.LongTextArea1__c;
   string EX2 = Sample.LongTextArea2__c;
   
   //Get sizes of Goal and Strategy in the event they are longer than 255 char and need to be truncated
   Integer size1 = EX1.length();
   Integer size2 = EX2.length();
   
   //If Goal or Strategy are longer than 255 characters they need to be truncated
   if(size1> 255){
         EX1= EX1.substring(0, 255);
   }
   if(size2 > 255){
         EX2 = EX2.substring(0, 255);
   }



I retrieve the length of the strings that were assigned the value of the Long Text Area (EX1 and EX2) and if those lengths are more than 255 characters then I truncated it with the substring method. If they are shorter than the maximum length of a Text Area (255) I do not adjust them at all.

Below is a link to the String Methods as defined by Salesforce.com.

http://www.salesforce.com/us/developer/docs/apexcode/index.htm 

Wednesday, April 11, 2012

Setting Default Values to Record - Using Visualforce to Override New Button

The goal is to set some default values to a record, there are times that you can not set the proper default value to fields when using the 'Set Default Value' option on a field. A lookup value can be a prime example and that is why I had to research this.

You need 2 things, an apex class that puts together the URL for the page, and a Visualforce page that basically outputs the URL that is generated in the class.

Below are my two items, the Visualforce page code as well as the class that it invokes.

Visualforce Page Code:

<apex:page standardController="Account_Child__c" extensions="DefaultAccountController" action="{!putDefault}">

</apex:page>

APEX Class Code:

 public class DefaultAccountController {
public DefaultAccountController(ApexPages.StandardController controller) {
    }
 
    public PageReference putDefault() {  
String tester = getAccount().Name;
//creating a page reference
PageReference pageRef;      
//Variables to hold the URL parameters  
String retURL =  ApexPages.currentPage().getParameters().get('retURL');
String retIDName =     ApexPages.currentPage().getParameters().get('CF00NC00000053NfO');      
String retAcctID =     ApexPages.currentPage().getParameters().get('CF00NC00000053NfO_lkid');
//Variables from the parent account
List <Account> acct = [select id, name, phone,industry from Account where id =:retAcctID];
string acctNum = acct[0].phone;
string acctInd = acct[0].industry;
//The URL that will be used in the new VF page
pageRef = new PageReference('/a0A/e?CF00NC00000053NfO='+retIDName+'&CF00NC00000053NfO_lkid='+retAcctID+'&Name='+acctNum+'&00NC00000053NfY='+acctInd+'&retUrl='+retURL);  
//adding nooverride to prevent infinite loop  
pageRef.getParameters().put('nooverride', '1');          
pageRef.setRedirect(true);          
//Item that is returned when this class is invoked is the PageRef URL
return pageRef;
}
} 

The highlighted items below should appear on a single line of code.

This is a link to the developer.force.com post that was helpful in creating this.

http://boards.developerforce.com/t5/General-Development/How-to-set-standard-field-to-a-Default-value/m-p/333595/highlight/true#M60358

Thursday, March 15, 2012

Entering a Specific Date in APEX

I was trying to assign a calendar date like this

q2.Calendar_Date__c = '03-14-2012';

That doesn't work! You have for create a new Date Instance like this....

q2.Calendar_Date__c = date.newInstance(2011, 03, 14);


This is the board the I found this on: (Thank you Bob Buzzard!)

http://boards.developerforce.com/t5/Apex-Code-Development/Date-Variable/m-p/197399/highlight/true#M33430

Friday, January 20, 2012

Salesforce to Salesforce Automation Trigger

Here are a couple links for some Salesforce to Salesforce trigger automation. The first is the basics on S2S that shows how to set it up and a quick snippet of code showing you how to share automatically with APEX. The second is a developer helping another developer work through some problems he had in the code, both of which may be helpful...

http://wiki.developerforce.com/page/An_Introduction_to_Salesforce_to_Salesforce

http://boards.developerforce.com/t5/Apex-Code-Development/Can-you-automate-record-sharing-in-Salesforce-to-Salesforce-with/m-p/324669/highlight/true#M57503

Monday, November 21, 2011

Custom Settings Update Trigger

Finally getting back into some more development... We had a situation where in order to make the code dynamic it made sense to use Custom Settings. I have never used them before today, there are Hierarchy and List Custom Settings. Hierarchy can only have one set of values per Profile or User, while List can have multiple values for a single Custom Setting.

The problem is that we need a way to dynamically add a 'Level' to a custom object based on the record type of the object. If a new record type is added all we want the sys admin to do is add a new custom setting and have the code do the rest. So long as the new custom setting and the record type have the same name the code will work.

First the code looks to the Custom Setting and pulls all values out of it and stores them in a list. Next we determine the size of the list, then we run a for loop that loops through if statement no more than the size of the list. If it finds that the field called 'Record_Type__c' in our Custom Setting called Level_Setting__c matched the name of the record type of the territory. (I had to use a formula field that just pulls the record name because I could not find a field on the custom object that was the name of the record type.) Finally if it finds a match it sets the custom field on the Custom Setting called Level__c equal to the text field Level__c on the Territory.

So long as the Record Type and the Level in the Custom setting are both in the correct order numerically and the record type names match up, this code working alongside the Custom Setting will work nicely.


trigger BeforeInsertUpdateLevelUpdateTrigger on Territory__c (before insert, before update) {

for(Territory__c t: trigger.new){

List<Level_Setting__c> levelSet = Level_Setting__c.getall().values();
//Pull values from the Custom Setting 'Level_Setting__c'

integer size = levelSet.size();
for(integer i=0; i<size; i++){
                      //Loop through the list of Level_Settings__c to find a match to the record type
if(levelSet[i].Record_Type__c == t.RecordTypeName__c ){
t.Level__c = levelSet[i].Level__c;
}
}
}
}