function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
alertusalertus 

Possible to do basic math in a visualforce email template or is custom apex needed?

I want to create a visualforce email template which displays the cost of various support contracts.  Something to the effect of:

 

Total Cost: 10,000.

Basic Package (5%) = $500

Premier Package (10%) = $1000

Contract Expiration Date: 8/1/2010

 

I have fields in our Account object for the total cost and the expiration date.  The basic and premier package costs are calculated by multiplying a percentage (either 5% or 10%) x Total Cost.  

 

Most of the documentation i've seen for the visualforce pages use custom controllers which are defined by <apex:page controller="somecontroller"> syntax.  But, when creating an email template this syntax is different.

 

Here is what i have currently:

 

<messaging:emailTemplate subject="test" recipientType="Contact" relatedToType="Account">
<messaging:plainTextEmailBody >
Account Name: {!relatedTo.Name}</br>
Expiration Date: {!relatedTo.Support_Contract_Expiration__c}<br/>
Total System Cost: {!relatedTo.Support_Contract_Basis__c}<br/>
</messaging:plainTextEmailBody>
</messaging:emailTemplate>

 

Using the "__c" I found i was able to access the custom fields in the account object which contain the expiration date and the total cost.

 

Questions:

* Is it possible to do simple multiplication in visual force w/o creating a custom apex component.

* If no, I'm not sure how to create a custom apex class or controller and pass the account details to it in order to then create get methods for the basic cost and premier cost.

 

Could someone point me in the right direction?

 

Thanks in advance.

 

 

 

 

Best Answer chosen by Admin (Salesforce Developers) 
alertusalertus

Hi David,

 

Thanks again for all your help.  I got it all working.  

 

Here are the various important code snippets for anyone else trying to do the same.  I bolded parts which were important.

 

 

VisualForce Email Template
 

<messaging:emailTemplate subject="Support Packages" recipientType="Contact" relatedToType="Account">
<messaging:htmlEmailBody >
Hi {!recipient.name},

<P>To continue to provide the highest level of service possible to our.. blah blah

<!-- DO NOT MODIFY THE LINE BELOW: IT INSERTS THE COST BREAKDOWN TABLE -->
<c:SupportContractPriceBreakdown componentValue="{!relatedTo.Name}"/>

<P>Enrollment in a support contract is not mandatory to continue... blah blah

</messaging:htmlEmailBody>
</messaging:emailTemplate>


Controller

The VisuaForce code assigned componentValue to the account name...

Then we use the <apex:attribute tag's assignTo to pass the account name to the Apex Class.

Note: all variables with {!varName} (like the ones in the table below) will be replaced with the value returned in the Apex class' getVarName() method.


<apex:component controller="supportContractPriceCalculator" access="global">

<apex:attribute name="componentValue" description="Attribute on the component."
type="String" required="required" assignTo="{!accountNameValue}"/>

<table>
<tr><td>Account Name:<td>{!accountNameValue}
<tr><td>Support Package Expires:&nbsp;&nbsp;<td>{!expirationDate}
<tr><td>Total System Cost:<td>${!totalCost}
<tr><td>Basic (7%):<td>{!basicPrice}
<tr><td>Premier (13%):<td>{!premierPrice}
<tr><td>Premier+API (17%):<td>{!premierPlusApiPrice}
</table>

</apex:component>

 

Apex Class
Important thing here was that when performing the soql query you have to list field in the Account object you want to be able to access.  I found this strange at first since you're creating an Account object... but i can see why they would do this for performance reasons.  In otherwords... there doesn't appear to be a way to do a Select * from Account where name = "Kansas".  Also, if you want to use a variable in an soql query you prefix it with a ':'.

 

I imagine there is a way to pass the Account object from VisualForce all the way up the chain rather then just the account name and then performing a soql query here...  I played around with it a bit and couldn't get it to work so just went back to this method for now.

 

public class supportContractPriceCalculator{

private Account contractAccount;
private String accountNameValue;

public supportContractPriceCalculator() {

}


public double getTotalCost() {
double retVal = 0;
if( contractAccount.Total_Cost__c != null ) {
retVal = contractAccount.Total_Cost__c;
}
return Double.valueOf(retVal);
}



public String getBasicPrice() {
return '$' + String.valueOf(getTotalCost() * 0.07);
}

public String getPremierPrice() {
return '$' + String.valueOf(getTotalCost() * 0.13);
}

public String getPremierPlusApiPrice() {
return '$' + String.valueOf(getTotalCost() * 0.17);
}

public String getExpirationDate() {
if( contractAccount.Expiration_Date__c == null ){
return 'Unknown';
}
else {
return contractAccount.Expiration_Date__c.format();
}
}

/**
* Alternate setter method which allows an account object to be passed in.
* Currently isn't used.
*/
public void setAccount( Account pAccount ) {
contractAccount = pAccount;
}

public Account getAccount() {
return contractAccount;
}

/**
* This method takes in a string and uses it to perform an soql query to get all the
* support contract related fields such as expiration date, total cost, current package etc.
* @param s is the account name.
*/
public void setAccountNameValue (String s) {
accountNameValue = s;
if( accountNameValue != null ) {
contractAccount = [select Name, Account.Expiration_Date__c, Account.Total_Cost__c from Account where Name = :s];
}
else {
contractAccount = new Account();
contractAccount.Name = 'No Account Selected';

}
}

public String getAccountNameValue() {
return contractAccount.Name;
}
}

 

Any feedback on how I implemented this would be appreciated.

All Answers

David VPDavid VP

Can't you just put some calculated fields on your object and merge those in your template ?

 

David

alertusalertus

Seems like there should be a better way to do this.  We're trying to avoid cluttering up our Account object.

 

Also, I could forsee wanting to run a promotion where we offer 10% off for a certain time period and this would then require creating even more fields.

 

Also, We are going to need to do some basic date formatting on the expiration date... I assume this can be done in visualforce.   Have you seen a visualforce tutorial which goes into apex controller examples specifically for email templates?

David VPDavid VP

The VisualForce developers guide explains how to do this :

 

http://www.salesforce.com/us/developer/docs/pages/Content/pages_email_templates_with_apex.htm

 

This should get you going.

 

 

David

 

 

alertusalertus

Hi David,

 

Thanks for the link.  It is very helpful.

 

The only piece that is missing for me is how do I pass the relatedTo Account info for the particular email template to the Apex code.  In the example provided, they use OSQL to get a list of all accounts starting with "Smith" (so no information is passed from email template -> apex controller -> apex class.  I need to pass the account information to the apex class so that I can lookup the Account's total cost and expiration date.

 

Any thoughts?

David VPDavid VP

I would try to pass the account id to the component via a component attribute. And query for the info you need in the component's controller. You can get the components attributes values into the controllers' variables by using 'assignTo'

 

Look it up in the docs, you'll see what I mean.

 

 

David

 

 

alertusalertus

Hi David,

 

Thanks again for all your help.  I got it all working.  

 

Here are the various important code snippets for anyone else trying to do the same.  I bolded parts which were important.

 

 

VisualForce Email Template
 

<messaging:emailTemplate subject="Support Packages" recipientType="Contact" relatedToType="Account">
<messaging:htmlEmailBody >
Hi {!recipient.name},

<P>To continue to provide the highest level of service possible to our.. blah blah

<!-- DO NOT MODIFY THE LINE BELOW: IT INSERTS THE COST BREAKDOWN TABLE -->
<c:SupportContractPriceBreakdown componentValue="{!relatedTo.Name}"/>

<P>Enrollment in a support contract is not mandatory to continue... blah blah

</messaging:htmlEmailBody>
</messaging:emailTemplate>


Controller

The VisuaForce code assigned componentValue to the account name...

Then we use the <apex:attribute tag's assignTo to pass the account name to the Apex Class.

Note: all variables with {!varName} (like the ones in the table below) will be replaced with the value returned in the Apex class' getVarName() method.


<apex:component controller="supportContractPriceCalculator" access="global">

<apex:attribute name="componentValue" description="Attribute on the component."
type="String" required="required" assignTo="{!accountNameValue}"/>

<table>
<tr><td>Account Name:<td>{!accountNameValue}
<tr><td>Support Package Expires:&nbsp;&nbsp;<td>{!expirationDate}
<tr><td>Total System Cost:<td>${!totalCost}
<tr><td>Basic (7%):<td>{!basicPrice}
<tr><td>Premier (13%):<td>{!premierPrice}
<tr><td>Premier+API (17%):<td>{!premierPlusApiPrice}
</table>

</apex:component>

 

Apex Class
Important thing here was that when performing the soql query you have to list field in the Account object you want to be able to access.  I found this strange at first since you're creating an Account object... but i can see why they would do this for performance reasons.  In otherwords... there doesn't appear to be a way to do a Select * from Account where name = "Kansas".  Also, if you want to use a variable in an soql query you prefix it with a ':'.

 

I imagine there is a way to pass the Account object from VisualForce all the way up the chain rather then just the account name and then performing a soql query here...  I played around with it a bit and couldn't get it to work so just went back to this method for now.

 

public class supportContractPriceCalculator{

private Account contractAccount;
private String accountNameValue;

public supportContractPriceCalculator() {

}


public double getTotalCost() {
double retVal = 0;
if( contractAccount.Total_Cost__c != null ) {
retVal = contractAccount.Total_Cost__c;
}
return Double.valueOf(retVal);
}



public String getBasicPrice() {
return '$' + String.valueOf(getTotalCost() * 0.07);
}

public String getPremierPrice() {
return '$' + String.valueOf(getTotalCost() * 0.13);
}

public String getPremierPlusApiPrice() {
return '$' + String.valueOf(getTotalCost() * 0.17);
}

public String getExpirationDate() {
if( contractAccount.Expiration_Date__c == null ){
return 'Unknown';
}
else {
return contractAccount.Expiration_Date__c.format();
}
}

/**
* Alternate setter method which allows an account object to be passed in.
* Currently isn't used.
*/
public void setAccount( Account pAccount ) {
contractAccount = pAccount;
}

public Account getAccount() {
return contractAccount;
}

/**
* This method takes in a string and uses it to perform an soql query to get all the
* support contract related fields such as expiration date, total cost, current package etc.
* @param s is the account name.
*/
public void setAccountNameValue (String s) {
accountNameValue = s;
if( accountNameValue != null ) {
contractAccount = [select Name, Account.Expiration_Date__c, Account.Total_Cost__c from Account where Name = :s];
}
else {
contractAccount = new Account();
contractAccount.Name = 'No Account Selected';

}
}

public String getAccountNameValue() {
return contractAccount.Name;
}
}

 

Any feedback on how I implemented this would be appreciated.
This was selected as the best answer
amr.rsamr.rs

Friends,

 

I'm working in Professional edition of Salesforce and i want to do similar math in VisualForce Page.

 

Unfortunately my edition doesn't have option for controller & classes.

 

Please guide me to proceed.

 

Thanks.

 

Regards,

AMR.