Showing posts with label visualforce. Show all posts
Showing posts with label visualforce. Show all posts

Tuesday, September 27, 2011

MultiSelect Picklist Solution for a VisualForce Page

I had to use a VF Page for Person Accounts. On adding an InputField bound to a picklist to the VisualForce Page - I kept getting this error 'The first validation error encountered was "Record Type ID: value not valid for the entity: Account".' on loading the VF Page. I had to resort to using a picklist solution as given here

But when it comes to MultiSelect Picklists, the UI was not much great, having multiselect = "True" on the VF SelectList Component - It just served the purpose. The UI required, using the Shift + a Select with your mouse or Ctrl + a Select with your mouse. Not very User-Friendly!! :(

A multiselect component which has two list boxes - one containing the available values and the other containing the chosen values and a VF Page which uses the same for displaying the multiselect values is worth a try. A User-Friendly Solution! Just works like how any multi-select field would behave on an Edit Page! :)

The component for MultiSelect can be used on the Person Account VF Page, just like any other component.

<c:MultiselectComponent AvailableList="{!availableList}" ChosenList="{!chosenList}"/>


The below code can be used to get the picklist values

Schema.DescribeFieldResult optionFieldDescription = Account.MultiSelect__c.getDescribe();
for(Schema.PicklistEntry pleOptions : optionFieldDescription.getPicklistValues()){
availableList.add(new SelectOption(pleOptions.getvalue(),pleOptions.getLabel()));
}


When there is a need for an update to the field value:
The multi-select picklist options are saved in salesforce as a string, separated by a semi-colon. A method can be used to combine the options before saving into the database.

private static String MULTIPICKLIST_SEPERATOR = ';';
private String combineOptions(List<SelectOption> values) {
String result = '';
for(SelectOption s: values) {
result = result == '' ? s.getValue() : result + MULTIPICKLIST_SEPERATOR + s.getValue();
}
return result.length() > 0 ? result.substring(0, result.length()): result;
}


The multiSelect component

<apex:component controller="MultiSelectComponentController">
<apex:attribute name="AvailableList" type="selectOption[]" description="Available List from the Page" assignTo="{!options}" required="True"/>
<apex:attribute name="ChosenList" type="selectOption[]" description="Chosen List from the Page" assignTo="{!selectedOptions}" required="True"/>

<script type="text/javascript">
function selection() {
selection();
}
function deselection() {
deselection();
}
</script>
<!-- Apex function called to move the selected values from available list to chosen list and vice versa -->
<apex:actionFunction name="selection" action="{!selecting}" reRender="multiselect"/>
<apex:actionFunction name="deselection" action="{!deselecting}" reRender="multiselect"/>
<apex:outputPanel id="panel">
<apex:pageBlockSection columns="4" >
<apex:selectList multiselect="true" size="5" value="{!selected}">
<apex:selectOption value="{!Available}"/>
<apex:selectOptions value="{!options}" />
<apex:actionSupport event="ondblclick" action="{!selecting}" rerender="panel" status="waitingStatus" />
</apex:selectList>
<apex:pageBlockSection columns="1">
<apex:commandButton reRender="panel" id="select" action="{!selecting}" value=">" status="waitingStatus"/>
<apex:commandButton reRender="panel" id="deselect" action="{!deselecting}" value="<" status="waitingStatus"/>
</apex:pageBlockSection>
<!-- An action status to show that the operation of moving between the lists is in progress--->
<apex:actionStatus id="waitingStatus" startText="Please wait..." stopText=""/>
<apex:selectList multiselect="true" size="5" value="{!deselected}">
<apex:selectOption value="{!Chosen}"/>
<apex:selectOptions value="{!selectedOptions}" />
<apex:actionSupport event="ondblclick" action="{!deselecting}" rerender="panel" status="waitingStatus" />
</apex:selectList>
</apex:pageBlockSection>
</apex:outputPanel>
</apex:component>


Corresponding Apex functions in the MultiSelectComponentController:

public void selecting() {
for(String toSelect: selected) {
Integer i = 0;
While(i<options.size()) {
if(options[i].getvalue()==toSelect) {
selectedOptions.add(new SelectOption(toSelect,toSelect));
options.remove(i);
}
i++;
}
}
}

public void deselecting() {
for(String toDeselect: deselected) {
Integer i = 0;
While(i<selectedOptions.size()) {
if(selectedOptions[i].getvalue()==toDeselect) {
options.add(new SelectOption(toDeselect, toDeselect));
selectedOptions.remove(i);
}
i++;
}
}
}

Monday, September 19, 2011

Calculate Case Age considering Salesforce Business Hours

Requirement being Salesforce Business Hours need to be considered while calculating a Case Age, and Business hours can change anytime:
Case 1: The Default Business Hours are Mon -9 am to 5 pm, tue -9 am to 5 pm, wed -9 am to 5 pm, thu -9 am to 5 pm, fri -9 am to 5 pm.
A case opened on Monday 10 am when checked on the following tuesday 9 am should show 8 hours
Case 2: The Default Business Hours are Mon -10 am to 6 pm, tue -8 am to 4 pm, wed -9 am to 6 pm, thu -9 am to 5 pm, fri -8 am to 4 pm
A case opened on Monday 10 am when checked on the following tuesday 9 am should show 9 hours

Also, if national holidays or public holidays [which might occur on weekdays] need to be excluded while calculating the Case - A scheduled apex is the only way. The following code snippet gives a solution to Case Age considering default Salesforce Business Hours.

global class ScheduledApexOnCase implements Schedulable {

global void execute(SchedulableContext sc) {

Date today = system.today();
Decimal caseAge;
List newCaseList = new List();

BusinessHours stdBusinessHours = [select id from businesshours where isDefault = true];
newCaseList = [Select id, CreatedDate, Status, CaseAge__c from Case]; //CaseAge__c is a Text field
for(Case eachCase: newCaseList) {
if(eachCase.Status != 'Closed')
caseAge = BusinessHours.diff(stdBusinessHours.id, eachCase.CreatedDate, today) / 1000 / 3600;
else
caseAge = BusinessHours.diff(stdBusinessHours.id, eachCase.CreatedDate, eachCase.ClosedDate) / 1000 / 3600;
eachCase.CaseAge__c = String.valueOf(caseAge) + ' hours';
}
update newCaseList;
}
}


Note:
Business Hours can be modified on Admin SetUp | Company Profile | Business Hours
BusinessHours.diff(stdBusinessHours.id, eachCase.CreatedDate, today) / 1000 / 3600; // This can be further divided by 8 or 9 hours to get the number of days [considering :working hours of 8 or 9 per day]
The ScheduledApex should run once in an hour to show the case age in number of hours