• cmarz1
  • NEWBIE
  • 0 Points
  • Member since 2009
  • Salesforce

  • Chatter
    Feed
  • 0
    Best Answers
  • 2
    Likes Received
  • 0
    Likes Given
  • 2
    Questions
  • 1
    Replies

Lets say you have two tasks A and B.  B is dependant on A.  I want B's Due Date to be X number of days after A is completed.  Right now with Action Plan B's start date is X number of day from the Action Plans start date.  This won't due and doesn't really make sense to me.  In the real world if task B is truly dependant on task A and task B tasks 4 days to complete than task B's due date should = A.CompletedDate + 5 days.

 

I have already modified Action Plan to record the actual completed date of a task, I added a custom field to APTasks that stores the date the user set as the due date when they completed the task.  So I have that date and now I need have the Action Plan create Task B using the Completed date of task A plus X number of days. 

 

Anyone know how to do this. I have been staring at the code for hours and I am not sure where in the ActionPlansTaskTriggerUtilities() class I need inject some code to get this to work.

  • October 04, 2012
  • Like
  • 2

I have a custom object which has a related list of Accounts. I want a button that can create a new Opportunity for any account in the list.  So I created a custom list button that is a URL.  Using this guide I am able to get the field names I need into the URL: http://salesforce.phollaio.com/2007/04/02/how_to_obtain_a_field_id/

 

The problem is none of the Account data is showing up in the URL only data from the custom object.  Below is a sample of the URL list button in setup:

 

https://naX.salesforce.com/006/e?opp3={!CustomObj__c.Name}&opp4_lkid={!Account.Id}

 

This should give me a new opportunity with the Name equal to the name of the Custom Object and linked to the Account ID selected in the related list but all I get is a new opportunity with the name of the custom object.  The account isn't filled in.  If I manually type in the URL it works.  It appears I am not able to access any of the related list object's data via a List Button.  Is this really the case or am I doing something wrong?

  • January 10, 2012
  • Like
  • 0

Lets say you have two tasks A and B.  B is dependant on A.  I want B's Due Date to be X number of days after A is completed.  Right now with Action Plan B's start date is X number of day from the Action Plans start date.  This won't due and doesn't really make sense to me.  In the real world if task B is truly dependant on task A and task B tasks 4 days to complete than task B's due date should = A.CompletedDate + 5 days.

 

I have already modified Action Plan to record the actual completed date of a task, I added a custom field to APTasks that stores the date the user set as the due date when they completed the task.  So I have that date and now I need have the Action Plan create Task B using the Completed date of task A plus X number of days. 

 

Anyone know how to do this. I have been staring at the code for hours and I am not sure where in the ActionPlansTaskTriggerUtilities() class I need inject some code to get this to work.

  • October 04, 2012
  • Like
  • 2

Dear salesforce.com users,

 

I want to share with you one Appex class that sorts a List<sObject> by any field in ascending or descending order. The List<sObject> is generated by any SOQL statement, so it can be made of custom and/or standard objects. 

 

Using this class is quite simple, and because I have written unit tests that validates 100% of the code you can easily use it in production sytems.

 

The class performs quite well because the sorting is done in memory (using Maps, Sets and Lists). It also detects if the sort has been done for this field so it does not need to resort (even if it is in reverse order).

 

Before going into details of the Appex class, let me show you how the class is used...

 

The VisualForce page:

Nothing fancy here... Just a page building a datatable with three columns and command buttons on the table headers to sort the data.

<apex:page controller="aaSorterContact">
<apex:form >
<apex:pageBlock >
<apex:pageBlockSection columns="1" ID="AjaxTable">
<apex:datatable value="{!List}" var="acc" Border="1" cellspacing="1" cellpadding="5">
<apex:column >
<apex:facet name="header">
<apex:commandButton action="{!SortByName}"

value="Sort By Name" rerender="AjaxTable" />
</apex:facet>
<apex:outputText value="{!acc.Name}" />
</apex:column>
<apex:column >
<apex:facet name="header">
<apex:commandButton action="{!SortByPhone}"

value="Sort By Phone" rerender="AjaxTable" />
</apex:facet>
<apex:outputText value="{!acc.Phone}" />

</apex:column>
<apex:column >
<apex:facet name="header">

<apex:commandButton action="{!SortByAccount}"

value="Sort By Account" rerender="AjaxTable" />
</apex:facet>
<apex:outputText value="{!acc.Account.Name}" />

</apex:column>
</apex:datatable>
</apex:pageBlockSection>
</apex:pageBlock>
</apex:form>
</apex:page>

The controller:

Couple things going in here, but that is just to make the page look nice... Nothing really to do with the sorting.

public class aaSorterContact {
private String sortedBy = null;
private Boolean sortAscending = null;
private AP_SortHelper sorter = new AP_SortHelper();
private List<Contact> sortedList = null;

public aaSorterContact() {
sorter.originalList = [SELECT Name, Phone, Account.Name FROM Contact];
}
public PageReference SortByName() {
setSortedBy('NAME');
sortedList = (List<Contact>) sorter.getSortedList('Name', sortAscending);
return null;
}
public PageReference SortByAccount() {
setSortedBy('ACCOUNT');
sortedList = (List<Contact>) sorter.getSortedList('Account.Name', sortAscending);
return null;
}
public PageReference SortByPhone() {
setSortedBy('PHONE');
sortedList = (List<Contact>) sorter.getSortedList('Phone', sortAscending);
return null;
}
public List<Contact> getList() {
if (sortedList == null) {
SortByName();
}
return sortedList;
}
private void setSortedBy(String value) {
if (sortedBy == value) {
sortAscending = !sortAscending;
} else {
sortAscending = true;
}
sortedBy = value;
}
}

 

Let me talk about the easy part first...

 

There are methods that answer the calls from the commandbuttons on the page:

 

  • SortByName
  • SortByAccount
  • SortByPhone

 

These methods follow the same structure:

setSortedBy('NAME');
sortedList = (List<Contact>) sorter.getSortedList('Name', sortAscending);
return null;

First, it calls a method setSortedBy() to find out the ascending or descending order. If the user clicks on a different button, the table is sorted ascending by that column, ortherwise the order is inverted from Ascending to descending and viceversa.

 

Second, it calls the method in the Appex class that does the sorting. (I will explain on detail how to use the Appex class, keep reading) :smileywink:

 

Finally, the controller's method returns a null value to the page.

 

The controller's constructor gets the list from the database.

public aaSorterContact() {
sorter.originalList = [SELECT Name, Phone, Account.Name FROM Contact];
}

Since the buttons use the rerendered propery (using AJAX), the class constructor is only called at the initial page load rather than every time the buttons are clicked, therefore the SOQL gets called only once regardless of how many times the data table gets sorted.

 

Finally, the more interesting part...

 

The Appex class that sorts:

You don't really need to understand how this class works to use it, but those of you who are interested...

public class AP_SortHelper {     // <ID, Position>
private Map<String, Integer> listPosition = null; // <FieldName, <FieldValues>>
private Map<String, List<String>> sortedFieldValuesPerFieldName = null; // <FieldName, <FieldValue, <IDs>>>
private Map<String, Map<String, List<String>>> sObjectIDsPerFieldNames = null;

// Properties
public List<sObject> originalList {get; set;}

// Constructor
public AP_SortHelper() {
originalList = null;
}// Public Method
public List<sObject> getSortedList(String fieldName, Boolean ascending) {
if (originalList == null) {
// Assume that originalList has a not NULL value.
// If the class who uses this method has not assigned a value it will get an Exception which
// needs to be handled by the calling class. // Force the exception...
originalList.clear();
} // Make field name uppercase
fieldName = fieldName.toUpperCase(); // Get sorted list
return makeSortedList(fieldName, ascending);
}
public List<sObject> getSortedList(List<sObject> originalList, String fieldName, Boolean ascending) {
this.originalList = originalList;
sortedFieldValuesPerFieldName = null;
return getSortedList(fieldName, ascending);
}

// Private Methods
private void InitializeFieldName(String fieldName) {
String sObjectID;
Integer position;
String fieldValue;
List<String> sObjectIDs = null;
Set<String> valuesForFieldSet = null; // Sets automatically omit duplicate values
List<String> valuesForFieldList = null;
Map<String, List<String>> sObjectIDsPerFieldValues = null;

// Make sortedFieldValuesPerFieldName
if (sortedFieldValuesPerFieldName == null) {
listPosition = new Map<String, Integer>();
sortedFieldValuesPerFieldName = new Map<String, List<String>>();
sObjectIDsPerFieldNames = new Map<String, Map<String, List<String>>>();
}

// Get (or create) map of sObjectIDsPerFieldValues
sObjectIDsPerFieldValues = sObjectIDsPerFieldNames.get(fieldName);
if (sObjectIDsPerFieldValues == null) {
sObjectIDsPerFieldValues = new Map<String, List<String>>();
sObjectIDsPerFieldNames.put(fieldName, sObjectIDsPerFieldValues);
}
if (!sortedFieldValuesPerFieldName.keySet().contains(fieldName)) {
// Objects need to be initialized
position = 0;
valuesForFieldSet = new Set<String>();
listPosition = new Map<String, Integer>();

for (sObject sObj : originalList) {
sObjectID = sObj.ID;
fieldValue = getValue(sObj, fieldName);

// Add position to list
listPosition.put(sObjectID, position++);

// Add the value to the set (sets rather than lists to prevent duplicates)
valuesForFieldSet.add(fieldValue);

// Get (or create) map of sObjectIDs
sObjectIDs = sObjectIDsPerFieldValues.get(fieldValue);
if (sObjectIDs == null) {
sObjectIDs = new List<String>();
sObjectIDsPerFieldValues.put(fieldValue, sObjectIDs);
}

// Add ID to sObjectIDs
sObjectIDs.add(sObjectID);
}

// Sort set items (Need to convert to list)
valuesForFieldList = new List<String>();
valuesForFieldList.addAll(valuesForFieldSet);
valuesForFieldList.sort();

// Now add it to the map.
sortedFieldValuesPerFieldName.put(fieldName, valuesForFieldList);
}
}
private List<sObject> makeSortedList(String fieldName, Boolean ascending) {
Integer position;
List<String> sObjectIDs = null;
List<String> valuesForFieldList = null; // Initialize objects
InitializeFieldName(fieldName); // Get a list of the same type as the "originalList"
List<sObject> outputList = originalList.clone();
outputList.clear(); // Get a list of sorted values
valuesForFieldList = sortedFieldValuesPerFieldName.get(fieldName);

// for each sorted value
for (String fieldValue : valuesForFieldList) {
// Get lisft of IDs
sObjectIDs = sObjectIDsPerFieldNames.get(fieldName).get(fieldValue);

// for each ID
for (String ID : sObjectIDs) {
// Get position in originalList
position = listPosition.get(ID); // Add each sObject to the list.
if ((ascending) || (outputList.size()==0)) {
outputList.add(originalList[position]);
} else {
outputList.add(0, originalList[position]);
}
}
}
return outputList;
}
private static String getValue(sObject sObj, String fieldName) {
// This returns the sObject desired in case the fieldName refers to a linked object.
Integer pieceCount;
String[] fieldNamePieces;

fieldNamePieces = fieldName.split('\\.');
pieceCount = fieldNamePieces.size();
for (Integer i = 0; i < (pieceCount-1); i++) {
sObj = sObj.getSObject(fieldNamePieces[i]);
}
return String.valueOf(sObj.get(fieldNamePieces[pieceCount-1]));
}

// Unit testing
/*
static testMethod void testSortCustomObject() {
List<TPValue__c> TPValues;
AP_SortHelper sorter = new AP_SortHelper();
String fieldName;

TPValues = [SELECT TPName__r.TPName__c, Value__c FROM TPValue__c LIMIT 50];
fieldName = 'Value__c';
testOrderedList(sorter.getSortedList(TPValues, fieldName, true), fieldName, true);

fieldName = 'TPName__r.TPName__c';
testOrderedList(sorter.getSortedList(TPValues, fieldName, true), fieldName, true);
}
*/
static testMethod void testSimpleField_Ascending() {
testSortingContacts('Name', true);
}
static testMethod void testSimpleField_Descending() {
testSortingContacts('Name', False);
}
static testMethod void testLookupField_Ascending() {
testSortingContacts('Account.Name', True);
}
static testMethod void testLookupField_Decending() {
testSortingContacts('Account.Name', False);
}
static testMethod void testMultipleCalls() {
AP_SortHelper sorter;
sorter = testSortingContacts(null, 'Name', true);
testSortingContacts(sorter, 'Name', False);
testSortingContacts(sorter, 'Account.Name', True);
testSortingContacts(sorter, 'Account.Name', False);
}
static testMethod void testForgotOriginalList() {
Boolean exceptionDetected = false;
AP_SortHelper sorter = new AP_SortHelper();
try {
sorter.getSortedList('Name', true);
} catch (NullPointerException e) {
exceptionDetected = true;
}
System.assert(exceptionDetected);
}
static testMethod void testPassingList() {
AP_SortHelper sorter = new AP_SortHelper();
List<Contact> contacts = [SELECT Name, Phone, Account.Name FROM Contact LIMIT 50];
List<Contact> sortedList = (List<Contact>) sorter.getSortedList(contacts, 'Name', true);
testOrderedList(sortedList, 'Name', true);
}
private static void testSortingContacts(string fieldName, Boolean isAscending) {
testSortingContacts(null, fieldName, isAscending);
}
private static AP_SortHelper testSortingContacts(AP_SortHelper sorter, string fieldName, Boolean isAscending) {
// If sorted is null,create it.
if (sorter == null) {
sorter = new AP_SortHelper();
sorter.originalList = [SELECT Name, Phone, Account.Name FROM Contact LIMIT 50];
}

// Sort list
List<Contact> sortedList = (List<Contact>) sorter.getSortedList(fieldName, isAscending); // Test sort order
testOrderedList(sortedList, fieldName, isAscending);

return sorter;
}
private static void testOrderedList(List<sObject> sortedList, string fieldName, Boolean isAscending) {
String lastValue = null;
String currentValue = null; for (sObject sObj : sortedList) {
currentValue = getValue(sObj, fieldName);
if ((lastValue != null) && (currentValue != null)) { String strDebug = '';
strDebug += '\n--------------------------------------------------------------';
strDebug += '\nSTART';
strDebug += '\n--------------------------------------------------------------';
strDebug += '\n[Ascending:'+isAscending+']';
strDebug += '\n[Previous:'+lastValue+'] [IsNull():'+(lastValue==null)+']';
strDebug += '\n[Current:'+currentValue+'] [IsNull():'+(currentValue==null)+']';
strDebug += '\n[CompareTo:'+(currentValue.compareTo(lastValue))+']';
strDebug += '\n--------------------------------------------------------------';
strDebug += '\nEND';
strDebug += '\n--------------------------------------------------------------';
System.debug(strDebug); if (isAscending) {
System.assertEquals(currentValue.compareTo(lastValue)>=0, true);
} else {
System.assertEquals(currentValue.compareTo(lastValue)<=0, true);
}
}
lastValue = currentValue;
}
}
}

 

How to use this class?

  1. Create an instance of this class AP_SortHelper()
  2. Assign the list to sort. Get this list using SOQL.
  3. Call the getSortedList() method which takes two fields:
    1. The name of the field as it was used in the SOQL
    2. The order (true for ascending, false for descending

 


For now, I have one question to the group...

 

This message is getting long... Is there a better place to post it? The way I see it, AppeXchange is applications not for independent utility classes.

Message Edited by andresperez on 01-28-2009 10:45 AM