Showing posts with label Apex. Show all posts
Showing posts with label Apex. Show all posts

Wednesday, July 23, 2014

Salesforce Flows, Visualforce, and Record Context

I was asked to find a solution for our field reps that would simplify the process of adding a completed task to their activities when they visited one of our retail stores. We keep our retail locations in Salesforce.com as contacts under a master account, which is shared with all users using a special account sharing rule.

The solution I came up with was a simple flow that limited users to a specific set of enterable information, pre-filling the date of the activity, and marking the task complete when the activity was committed. Literally, the only thing they needed to do was select a picklist value and enter a comment about their visit. It was exactly what we were looking for.

Salesforce.com Flow

The thing I didn't like about the flow was that when a user entered it, context kind of got lost -- meaning they started out from a contact record, but didn't have a good visual cue about where they were once they got into the flow.

Sure, the flow was simple, but to me it's still poor UX not to know the context of your work at all times. So, I decided to use a Visualforce page to solve the problem:


<apex:page standardController="Contact">

<apex:sectionheader title="Add Retail Store Visit" subtitle="{!Contact.Name}"></apex:sectionheader>

<flow:interview name="Retail_Store_Visit">
<apex:param name="vContact" value="{!Contact.Id}"></apex:param>
<apex:param name="vAccount" value="{!Contact.Account.Id}"></apex:param>
</flow:interview>
</apex:page>


The trouble with this was that after the flow data was committed, the user would be returned to the beginning of the flow. That's definitely not what I wanted. I needed to go back to the contact record I started from.

So I added the finishLocation attribute to the <flow:interview></flow:interview> component tag. It would make sense that if I passed the Contact Id, the flow should return to the Contact record:


<flow:interview name="Retail_Store_Visit" finishLocation="{!URLFOR('/' + Contact.Id)}">
<apex:param name="vContact" value="{!Contact.Id}"></apex:param>
<apex:param name="vAccount" value="{!Contact.Account.Id}"></apex:param>
</flow:interview>


Nope. That didn't do it. When the flow is entered, context gets lost, so Salesforce doesn't really know where the user is anymore. Even using the vContact Apex parameter didn't work.

I searched help documentation for an answer, but it wasn't all that helpful. I searched Communities and came up empty. Then I searched the Salesforce Stack Exchange, where the only solution I could find was really convoluted, and didn't even come close to solving my problem. I was vexed.

Taking a break to get a cup of coffee, a solution dawned on me that turned out to be pretty darned simple. All I needed was an Apex variable in the page, which I could pass to the finishLocation component tag once the flow was complete. Oddly enough, this method is not documented -- at least not that I could find.

So...the final Visualforce page:


<apex:page standardController="Contact">
<apex:variable var="theContact" value="{!Contact.Id}"></apex:variable>

<apex:sectionheader title="Add Retail Store Visit" subtitle="{!Contact.Name}"></apex:sectionheader>

<flow:interview name="Retail_Store_Visit" finishLocation="{!URLFOR('/' + theContact)}">
<apex:param name="vContact" value="{!Contact.Id}"></apex:param>
<apex:param name="vAccount" value="{!Contact.Account.Id}"></apex:param>
</flow:interview>
</apex:page>


Using the Apex variable, users where returned to the contact record they started from, and could see the task they just added in the activity history related list. This was exactly what I was looking for. Everyone was happy.

If you need to return to a starting point when using flows and Visualforce pages, consider giving this solution a try.

Friday, October 19, 2012

Making a Field Appear Required on a Visualforce Page

I've been working on a force.com app with the requirement that a user must enter a valid email address on a Visualforce page before being able to save a record, but they must also be able to insert the related contact's email address by clicking a button instead of having to leave the edit page to go find it. That seemed simple enough.

Visual Force Page Example

In my original Visualforce page, it seemed logical that if I set the recipient email field as required, all would work as expected. However, defining the field as required prevented my custom action in my page controller from firing and entering the email address.

Original Page Controller




public class GiftCardTestController {

private ApexPages.StandardController std;
public String cEmail {get;set;}
public Gift_Card_Order__c gc {get;set;}
public GiftCardTestController(ApexPages.StandardController stdCtrl) {
std = stdCtrl;
}

//selects the email address of the related contact
//and inserts into recipient email field.
public void fillEmail() {
gc = (Gift_Card_Order__c)std.getRecord();
cEmail = [select Id, Email from Contact where Id = :gc.Contact__c].Email;
gc.Recipient_Email__c = cEmail;
}
}


Original Visualforce Page




<apex:page standardController="Gift_Card_Order__c" extensions="GiftCardTestController" title="Gift Card Test">
<apex:form>
<apex:pageblock title="Gift Card" mode="edit">

<apex:pageblockbuttons location="top">
<apex:commandbutton action="{!save}" value="Save"></apex:commandbutton>
<apex:commandbutton action="{!cancel}" value="Cancel"></apex:commandbutton>
<apex:commandbutton action="{!fillEmail}" value="Fill Email"></apex:commandbutton>
</apex:pageblockbuttons>

<apex:pageblocksection title="Email Info" columns="1">
<apex:inputfield value="{!Gift_Card_Order__c.Contact__c}"></apex:inputfield>
<apex:inputfield value="{!Gift_Card_Order__c.Recipient_Email__c}" required="true"></apex:inputfield>
</apex:pageblocksection>

</apex:pageblock>
</apex:form>
</apex:page>


In this example, the fillEmail() action should select the related contact email address, and put the value in the Recipient_Email__c field so the user can see it.

But it's not that simple it seems. When the field had the required="true" attribute set, the action would not fire because all validation is done on the client side and the page never posts back to the server -- so the controller action never gets called.

So after some digging and asking for help on the Salesforce discussion boards, the solution was to make the Recipient_Email__c appear as if it's required on the page (though it's really not), and add a new save method to my controller to handle field validation on the server side when the record gets saved.

New Page Controller




public class GiftCardTestController {

private ApexPages.StandardController std;
public String cEmail {get;set;}
public Gift_Card_Order__c gc {get;set;}
public GiftCardTestController(ApexPages.StandardController stdCtrl) {
std = stdCtrl;
}

public void fillEmail() {
gc = (Gift_Card_Order__c)std.getRecord();
cEmail = [select Id, Email from Contact where Id = :gc.Contact__c].Email;
gc.Recipient_Email__c = cEmail;
}

// add custom save method...
public pageReference save() {
gc = (Gift_Card_Order__c)std.getRecord();

// if the recipient email is null, add an error to the field
// and return null to remain on the current page...
if(gc.Recipient_Email__c == null) {
gc.Recipient_Email__c.addError('A valid email address is required.');
return null;
}

// otherwise, the field is filled, so it's okay to redirect to view page.
// standard field validation will check for valid email format.
else {
return std.save();
}
}
}


New Visualforce Page




<apex:page standardController="Gift_Card_Order__c" extensions="GiftCardTestController" title="Gift Card Test">
<apex:form>
<apex:pageblock title="Gift Card" mode="edit">
<apex:pageblockbuttons location="top">
<apex:commandbutton action="{!save}" value="Save"></apex:commandbutton>
<apex:commandbutton action="{!cancel}" value="Cancel"></apex:commandbutton>
<apex:commandbutton action="{!fillEmail}" value="Fill Email"></apex:commandbutton>
</apex:pageblockbuttons>

<apex:pageblocksection title="Email Information" columns="1">
<apex:inputfield value="{!Gift_Card_Order__c.Contact__c}"></apex:inputfield>

<!-- The updated pageblocksectionitem -->
<apex:pageblocksectionitem>
<apex:outputlabel>Email Recipient</apex:outputlabel>
<apex:outputpanel layout="block" styleClass="requiredInput">
<apex:outputpanel layout="block" styleClass="requiredBlock"></apex:outputpanel>
<apex:inputfield value="{!Gift_Card_Order__c.Recipient_Email__c}"></apex:inputfield>
</apex:outputpanel>
</apex:pageblocksectionitem>

</apex:pageblocksection>
</apex:pageblock>
</apex:form>
</apex:page>


Notice the <apex:pageblocksectionitem></apex:pageblocksectionitem> code to replace the original field. This is how we make the field appear with the "required" bar. A nifty trick that took some digging to discover. Hopefully this post saves someone else the time it took me to figure it out -- and me the time when I forget it.

For convenience, here's a Github Gist: https://gist.github.com/4595878