Appirio's Tech Blog

Tuesday, February 23, 2010

Relationship Lookup Objects in Triggers are NULL?

Jeff Douglas

I see this question once in awhile on the Salesforce.com message boards so I thought I’d put something together. So in this scenario you have a Sales_Order__c custom object which has a lookup relationship to Opportunity. When processing the Sales_Order__c records in your trigger, you want to access some fields on the Opportunity via the relationship. Your trigger might look something like this:
trigger SalesOrderUpdate on Sales_Order__c (before update) {

for (Sales_Order__c so : Trigger.new) {
System.debug('Opportuny Id: '+so.Opportunity__c);
// Opportunity__r will be NULL
System.debug('Opportuny: '+so.Opportunity__r.StageName);
}

}

However, every time you run the Trigger, the Opportunity is null even though there is a valid Id in Opportunity__c. The reason is that for scalability, the Force.com platform doesn’t perform an in-memory lookup for each relationship in your object. You need to do that yourself. The good thing is that the solution is relatively painless and is safe for bulk transactions.

In the trigger below you first want to create a set of unique Opportunity Ids that are in the trigger context. You then use those Ids to query and create a map with the Opportunity Id as the key and the Opportunity object as the value. Then when you process your Sales_Order__c records you can access the Opportunity object from the map by its Id (line 14).
trigger SalesOrderUpdate on Sales_Order__c (before update) {

// create a set of all the unique opportunity ids for SOQL below
Set oppIds = new Set();
for (Sales_Order__c so : Trigger.new)
oppIds.add(so.Opportunity__c);

// create a map so that Opportunity is locatable by its Id (key)
Map oppsMap = new Map(
[SELECT Amount, StageName FROM Opportunity WHERE Id IN :oppIds]);

for (Sales_Order__c so : Trigger.new) {
// fetch the Opportunity from the map by its Id
System.debug('Opportunity: '+oppsMap.get(so.Opportunity__c).StageName);
// perform some type of business operation now...
}

}

Monday, February 22, 2010

Displaying Currencies in a SFDC Email Template

Glenn Weinstein

A few weeks ago I posted here about displaying currencies in a SFDC formula field or Visualforce page.  The key challenge is preserving the comma separators and ensuring exactly 2 decimal places, so that, for example, you see USD 2,978.10 and not USD 2978.1.  To do this, I wrote an elaborate formula, which could be used in defining a custom field to go alongside your original currency field.

I’ve now extended the concept in two important ways:

  • I want to display currencies in SFDC email templates (specifically Visualforce templates).  Emails just don’t look right when you are talking about currency and display “2978.1”. 
  • I don’t want to have to add a custom formula field for every currency field.  I’d rather have some kind of generic solution that I can use ad-hoc when creating new email templates, without having to modify the data model.

I actually wish SFDC had a built-in formula function for this, like Javascript’s toFixed() function, that let you specify exactly how many decimal places to display.  But until they add something like that, we’ll need to resort to some trickery.

The approach is actually fairly simple – wrap my elaborate SFDC formula into a Visualforce custom component.  I’ve named it asCurrency.  To use it in a Visualforce email template, all I have to do is wrap my currency field in an asCurrency tag:

<c:asCurrency amount="{!relatedTo.Some_Currency_Field__c}" />

That’s reasonably clean.  I’m happy enough for now!

Here’s the source code for the Visualforce custom component:

<apex:component access="global">

  <!--

  (c) 2010 Appirio

  Render a field as currency, that is, with exactly two decimal places, and comma separators for 3-digit groupings.

  (Yes, I realize not all global currencies should be rendered this way.  But for our purposes, it works.)

  This is needed because, frustratingly, Visualforce can't do this natively - it tends to render Currency field with
  the minimal decimal places, e.g. USD 1,200.20 is rendered as USD 1200.2, which looks really funny.

  Note that the "amount" attribute is typed as "Decimal", because SFDC barks at "Currency" - if you actually
  try to pass in a Currency field, the compiler claims the field is "BigDecimal" (a non-existent Apex type...
  sigh) but it will accept Currency fields if we type the attribute as "Decimal".  Weird, but.... whatever works.

  2/21/10     Glenn Weinstein     Original

  -->

<apex:attribute name="amount"
    type="Decimal"
    description="The value to be rendered as currency."  
  />

  {!IF(
    amount >= 1000000,
    TEXT(FLOOR(amount / 1000000)) & ",",
    "") &
  IF(
    amount >= 1000,
    RIGHT(TEXT(FLOOR(amount / 1000)), 3) & ",",
    "") &
  RIGHT(TEXT(FLOOR(amount)), 3) & "." &
  IF(
    MOD(amount, 1) * 100 < 10,
    "0" & TEXT(ROUND(MOD(amount, 1), 2) * 100),
    TEXT(MIN(ROUND(MOD(amount, 1), 2) * 100, 99))
  )}

</apex:component>

Be sure to name your custom component “asCurrency”.  Note, we need the access=”global” bit (in the top-level apex:component tag) in order for our component to be available to Visualforce email templates.

Friday, February 5, 2010

Displaying Currencies In a SFDC Formula Field or Visualforce

Glenn Weinstein

I wanted to put a Formula field on the PSE Project detail page to show a red or green flag next to a currency amount, based on some criteria.

The flag image is the easy part.  The hard part is displaying the currency amount with proper formatting (commas and decimal points).  The TEXT() formula function results in a straight number, e.g. $1,532,311.78 turns into 1532311.78.

After much research, I determined there's no easy way to format numbers in conjunction with TEXT().  So I resorted to brute force, and wrote my own formula.  After much trial-and-error, I'm pretty sure I've perfected it. 

IF(
  someCurrencyField__c >= 1000000,
  TEXT(FLOOR(someCurrencyField__c / 1000000)) & ",",
  "") &
IF(
  someCurrencyField__c >= 1000,
  RIGHT(TEXT(FLOOR(someCurrencyField__c / 1000)), 3) & ",",
  "") &
RIGHT(TEXT(FLOOR(someCurrencyField__c)), 3) & "." &
IF(
  MOD(someCurrencyField__c , 1) * 100 < 10,
  "0" & TEXT(ROUND(MOD(someCurrencyField__c , 1), 2) * 100),
  TEXT(MIN(ROUND(MOD(someCurrencyField__c , 1), 2) * 100, 99))
)

Just replace someCurrencyField with your currency field, and you'll get back a nicely formatted number with 2 decimal points.  Notes:

  • Works for numbers up to 999,999,999.99.  (If you need higher, just add another IF() clause.)
  • Formula above assumes a multi-currency org.  If not, replace CurrencyIsoCode with a hard "$" string.
  • The last IF() clause is particularly tricky, to ensure we always get 2 digits to the right of the decimal point. 
  • That final MIN() is to guard against situations where someCurrencyField extends to more than 2 decimal points, to avoid inadvertently rounding up to 100 (we don't want, for example, $56,314.9999999 to come out as $56,314.100).
If you're curious, I use the above snippet in a formula that includes a red or green flag, based on some criteria.  Here is the (anonymized) formula, along with the IMAGE() formula to display the flag:

IF(
  something__c > somethingElse__c,
  IMAGE("/img/samples/flag_red.gif", "Red"),
  IMAGE("/img/samples/flag_green.gif", "Green")
  ) & " "  &
TEXT(CurrencyIsoCode) & " " &
/* copy in currency display code from above */

Then, pull the source field off your page layout, and drop in your new formula field.  This technique adds some visual enhancement to otherwise-tedious looking displays of numbers:

Capture

I like putting the flags inline with the numbers - the flags help you quickly assess whether a number is "good" or "bad".  And they look great as a column in a View list.

Note that you can run into a similar issue with Visualforce merge fields.  And if you're using Visualforce for an email template, you can't even rely on solving formatting issues via the controller.  But there is a much more straightforward solution to currency formatting in Visualforce, using the apex:outputText tag in conjunction with apex:param.  Here's an example:

<apex:outputText value="{0, number, ###,###,###,##0}">
  <apex:param value="{!relatedTo.someCurrencyField__c}"/>
</apex:outputText>

Per the Visualforce Developer's Guide, you can use the same syntax as the Java MessageFormat class for your apex:outputText formatting string.  Also, if you're showing currency (as I am here), you may want to precede your apex:outputText with a {!relatedTo.CurrencyIsoCode} merge field.

 
2006-2012 Appirio Inc. All rights reserved.
Appirio.com | Support | Resource Center | Contact | Careers | Privacy Policy