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
Tasia Demuth 4Tasia Demuth 4 

Controller for visualforce email template

Hi all, 

I am still learning writing apex code and could use some help on a controller I am writing for a visualforce email template. 

What I am trying to do is use a visualforce component to list all active contracts related to an account when a case is declined. 

Everything seems to work EXCEPT the component lists EVERY active contract not just those associated with the account. 

Any help would be greatly appreciated!!

Thanks,
Tasia

My controller looks like this: 

public class FindActiveContracts {
    private final List<Contract> contracts;
    public FindActiveContracts(){
        contracts = [select Name from Contract where status='Activated' AND EndDate > TODAY AND StartDate < TODAY AND AccountId IN (SELECT Id from Account Where Id =:case.accountId)];
        }
    public List<Contract> getActiveContracts() {
        return contracts;
    }
}

My component looks like this: 
<apex:component controller ="FindActiveContracts" access="global">
    <apex:dataTable value="{!ActiveContracts}" var="s_contract">
    <apex:column>
    <apex:facet name="header">Service Name</apex:facet>
        {!s_contract.Name}
    </apex:column>
</apex:dataTable>
</apex:component>

My email template looks like this: 
<messaging:emailTemplate subject="Your Ask E Source request on {!relatedTo.Subject}"
recipientType="Contact" 
relatedToType="Case"
replyTo="emily_hunsicker@esource.com">
<messaging:htmlEmailBody >        
    <html>
        <body>
         <STYLE type="text/css">
               TH {font-size: 11px; font-face: arial;background: #CCCCCC; border-width: 1;  text-align: center } 
               TD  {font-size: 11px; font-face: verdana } 
               TABLE {border: solid #CCCCCC; border-width: 1}
               TR {border: solid #CCCCCC; border-width: 1}
         </STYLE>
                  <font face="arial" size="2">
    <p>Hi {!recipient.Name},</p>
    <p>Thanks for your recent Ask E Source request on {!relatedTo.Subject}. We have done research about this topic as part of our E Source {!relatedTo.ProductLookup__r.Name}. Unfortunately, {!recipient.Account_Name__c} is not a member of that service, so we will not be able to answer your question at this time.</p>
        <p>Our <a href="https://www.esource.com/questions-faq">Ask E Source FAQ</a> describes the general topics that are covered under each of our services; you currently have access to these services:</p>
        <c:SetActiveContracts />                 
       <p>If you’d like a personal overview of your membership, please <a href="{!recipient.CSD_Calendly_Link__c}">contact us</a> to sign up for a 30-minute walk-through with your Customer Success Director, {!recipient.CSD_Name__c}. I have copied both {!recipient.CSD_First_Name__c} and {!recipient.BDD_Name__c}, your sales contact, on this message. 
 </p> 
       <p> Best,</p>
       <p> Emily Hunsicker</p>
       <p> Customer Success Coordinator, E Source</p>       
 </font>
       
        </body>
    </html>
Best Answer chosen by Tasia Demuth 4
Alain CabonAlain Cabon
My class FindActiveContracts above is different and you could try to get the contracts directly like below.

The mecanism of the contructor is not the same as the "getter".  

{!contracts} can use the public object as you defined and more sureley a "getter" method like:  public List<Contract> getContracts(){
 
public class FindActiveContracts {
    public Id caseAccountId {get;set;}
    public List<Contract> getContracts(){     
        return [select Name from Contract where status='Activated' AND EndDate > TODAY AND StartDate < TODAY AND AccountId = :caseAccountId ];
    }
}
There was a typo with an extra ")".
 

All Answers

Alain CabonAlain Cabon
Hi Tasia,

Your code seems truncated (not a complete copy/paste) so perhaps already solved.
The idea is to pass the case AccountId as an attribute of the component.

New attribute : name="caseAcctId"  assignTo="{!caseAccountId }"

SetActiveContracts:
<apex:component controller ="FindActiveContracts" access="global">
<apex:attribute name="caseAcctId" type="Id" description="account id of the case" assignTo="{!caseAccountId }"/>

New attribute usable for the call of the component: 
<messaging:emailTemplate subject="Your Ask E Source request on {!relatedTo.Subject}"
recipientType="Contact" 
relatedToType="Case"
replyTo="emily_hunsicker@esource.com">
<messaging:htmlEmailBody >        
...
<c:SetActiveContracts   caseAcctId="{!relatedTo.AccountId}"  />

At the end, you can filter your query correctly logically with the case account id:
public class FindActiveContracts {
 
    public Id caseAccountId {get;set;}

    public List<Contract> getContracts(){     
        return [select Name from Contract where status='Activated' AND EndDate > TODAY AND StartDate < TODAY AND AccountId = :caseAccountId)];
    }

}

 
Tasia Demuth 4Tasia Demuth 4
Hi Alain, 

Thank you very much. I feel like we are super close! I did provide all of my code the first time around, so maybe I am missing more lines of code? I am still having a hard time getting the Account Id to be set. 

Here is my controller - I added the attribute

<apex:component controller ="FindActiveContracts" access="global">
    <apex:attribute name="caseAcctId" type="Id" description="account id of the case" assignTo="{!caseAccountId}"/>
    <apex:dataTable value="{!ActiveContracts}" var="s_contract">
    <apex:column>
    <apex:facet name="header">Service Name</apex:facet>
        {!s_contract.Name}
    </apex:column>
</apex:dataTable>
</apex:component>

Here is my email template: 
<messaging:emailTemplate subject="Your Ask E Source request on {!relatedTo.Subject}"
recipientType="Contact" 
relatedToType="Case"
replyTo="emily_hunsicker@esource.com">
<messaging:htmlEmailBody >        
    <html>
        <body>
         <STYLE type="text/css">
               TH {font-size: 11px; font-face: arial;background: #CCCCCC; border-width: 1;  text-align: center } 
               TD  {font-size: 11px; font-face: verdana } 
               TABLE {border: solid #CCCCCC; border-width: 1}
               TR {border: solid #CCCCCC; border-width: 1}
         </STYLE>
                  <font face="arial" size="2">
    <p>Hi {!recipient.Name},</p>
    <p>Thanks for your recent Ask E Source request on {!relatedTo.Subject}. We have done research about this topic as part of our E Source {!relatedTo.ProductLookup__r.Name}. Unfortunately, {!recipient.Account_Name__c} is not a member of that service, so we will not be able to answer your question at this time.</p>
        <p>Our <a href="https://www.esource.com/questions-faq">Ask E Source FAQ</a> describes the general topics that are covered under each of our services; you currently have access to these services:</p>
        <c:SetActiveContracts caseAcctId="{!relatedTo.AccountId}"  />                 
       <p>If you’d like a personal overview of your membership, please <a href="{!recipient.CSD_Calendly_Link__c}">contact us</a> to sign up for a 30-minute walk-through with your Customer Success Director, {!recipient.CSD_Name__c}. I have copied both {!recipient.CSD_First_Name__c} and {!recipient.BDD_Name__c}, your sales contact, on this message. 
 </p> 
       <p> Best,</p>
       <p> Emily Hunsicker</p>
       <p> Customer Success Coordinator, E Source</p>       
 </font>
       
        </body>
    </html>

Here is my public class

public class FindActiveContracts {
    private final List<Contract> contracts;
    public Id caseAccountId {get;set;}
    public FindActiveContracts(){
        contracts = [select Name from Contract where status='Activated' AND EndDate > TODAY AND StartDate < TODAY AND AccountId =:caseAccountId];
        }
    public List<Contract> getActiveContracts() {
        return contracts;
    }
}

But, there are no contracts listed in my email template when I test with a Send Test and verify merge fields. The other merge fields work, but the table where the contracts should appear is empty. 

I really appreciate you taking the time to help me. 

Tasia

 
Alain CabonAlain Cabon
My class FindActiveContracts above is different and you could try to get the contracts directly like below.

The mecanism of the contructor is not the same as the "getter".  

{!contracts} can use the public object as you defined and more sureley a "getter" method like:  public List<Contract> getContracts(){
 
public class FindActiveContracts {
    public Id caseAccountId {get;set;}
    public List<Contract> getContracts(){     
        return [select Name from Contract where status='Activated' AND EndDate > TODAY AND StartDate < TODAY AND AccountId = :caseAccountId ];
    }
}
There was a typo with an extra ")".
 
This was selected as the best answer
Tasia Demuth 4Tasia Demuth 4
Thank you, Alain!! That did it. 

You are super helpful as always. 

One other question that you may be able to help me with - when I do a verify merge fields and send test email, the template works, but then when I send a test email to myself, the recipient fields do not populate. Do you know why that may be? 

Thanks again for your help!
Tasia
Alain CabonAlain Cabon
Hi Tasia,

You are a user perhaps for your test and not a contact (without warning).

A very common recipientType is "Contact
https://developer.salesforce.com/docs/atlas.en-us.pages.meta/pages/pages_compref_messaging_emailTemplate.htm

The alternative recipientType="User" is used in the code below:
https://www.simplysfdc.com/2016/07/salesforce-visualforce-email-template.html

With these constraints for a related type User:
Allow Email Templates to use Related to User info and test
https://success.salesforce.com/ideaView?id=08730000000kwNBAAY

Best regards
​​​​​​​Alain
Alain CabonAlain Cabon
The related type is not the recipient type above for the constraints.
 
Tasia Demuth 4Tasia Demuth 4
One more question - I am so sorry about all these questions. I am really trying to do it by myself first, but I am getting stuck.

What would my test class look like? 

This is what I got, but it is not covering the code. 

@isTest 
private class FindActiveContractsTest{
    static testMethod void validateFindActiveContracts() {
        
        Test.startTest();
    
        //Create an account
        Account a = new Account(Name='SF Test Account');
        insert a;

       //Create a test Contract
        Contract testContract = new Contract(Name = 'TestContract', ContractType__c='Standard',    ContractTerm=12, StartDate=date.newInstance(2019,01,01), AccountId=a.id);
        insert testContract; 
        List<Contract> contracts=[select Name from Contract where EndDate > TODAY AND StartDate < TODAY AND AccountId =:a.Id AND ContractType__c='Standard'];
        System.assert(contracts.size() > 0);
        System.assertEquals('TestContract', contracts[0].Name);
        System.assertEquals('Standard',contracts[0].ContractType__c);    
        Test.stopTest();
    }
}

Thank you again for all your help. 
Alain CabonAlain Cabon
It is not a trigger here so you need an explicit initialization and a call just after the creations of the account and its contract.
...
insert testContract; 
FindActiveContracts  fc  = new FindActiveContracts ();  
fc.caseAccountId         = a.Id;  
List<Contract> contracts = fc.getContracts();  // don't use the SOQL query here
...
Tasia Demuth 4Tasia Demuth 4
Omg yay! That makes sense! Thank you sooo much, Alain - you are the best!
Alain CabonAlain Cabon
You are welcome and successful continuation.
There is always a different matter in your problems and not many people are trying the email templates with apex controllers (quite complicated).

Best regards
Alain