Use a custom controller in Visualforce template

apexcontrollervisualforcevisualforce-componentvisualforce-template

I am trying to run an email template from the Contract object, but in the email (or an attached PDF) print a list of related cases and specified fields for the cases. I've managed to do this successfully on a visualforce page, but trying to use the same idea in an email template has not worked out. It's as though the query is not receiving the contract Id but I cannot figure out how to pass it so it works. Maybe I'm barking up the wrong tree though.

Visualforce Email Template:

<messaging:emailTemplate recipientType="User" 
    relatedToType="Contract"
    subject="Test {!relatedTo.ContractNumber}">
    
<messaging:attachment renderAs="PDF" filename="claims-list.pdf">
<html>
    <body>
        <div>
            <h3>CLAIMS</h3>
            
            <c:StatementofClaims Contract="{!relatedTo.Id}" policyref="{!relatedTo.Id}" />
            
            <table>
            
            </table>
        </div>
        <div>
            <p></p>
        </div>
    </body>
</html>
</messaging:attachment>    

<messaging:plainTextEmailBody >
test mail

{!relatedTo.Id}


<c:StatementofClaims policyref="{!relatedTo.Id}" />
</messaging:plainTextEmailBody>
</messaging:emailTemplate>

Visualforce Component:

<apex:component layout="none" access="global" controller="ClaimsListController2">
<apex:attribute name="Contract" description="Statement of Claims" type="Contract" />
<apex:attribute name="policyref" description="" type="id" assignTo="{!policyId}" />
<html>
    <style>
        .claimRecordsTable {
            border: 1px solid black;
        }
        .claimRecordTableCells {
            border-top: 1px solid black;
            border-bottom: 1px solid black;
            border-left: 1px solid black;
        }
    </style>
    <body>
        <div>
            
            {!testReturn2}
            <p><apex:outputText ><apex:param value="{!testReturn2}" /></apex:outputText></p>
            <table id="claimTableInfo" width="100%" rowClasses="odd,even" styleClass="claimRecordsTable" border="solid" cellpadding="5px">
                <thead>
                    <tr>
                        <th style="text-align: left; border-collapse:collapse; width:20%;">Claim No.</th>
                        <th style="text-align: left; border-collapse:collapse; width:20%;">Date Notified</th>
                        <th style="text-align: left; border-collapse:collapse; width:20%;">Repaired or Replaced</th>
                        <th style="text-align: left; border-collapse:collapse; width:20%;">Amount if Replaced</th>
                        <th style="text-align: left; border-collapse:collapse; width:20%;">Claim Status</th>
                    </tr>
                </thead>
                <tbody>
                    <apex:repeat value="{!claims}" var="claim">
                        <tr>
                            <td class="claimRecordsTable" width="170">{!claim.CaseNumber}</td>
                            <td class="claimRecordsTable" width="170"><apex:outputText value="{0,Date,dd/MM/yy}"><apex:param value="{!claim.Claim_Form_Recevied_Date__c}" /></apex:outputText></td>
                            <td class="claimRecordsTable" width="170">{!claim.Reason}</td>
                            <td class="claimRecordsTable" width="170">{!claim.Claim_Cost__c}</td>
                            <td class="claimRecordsTable" width="170">{!claim.status}</td>
                        </tr>
                    </apex:repeat>
                </tbody>
            </table>
            
            <table>
            
            </table>
        </div>
        <div>
            {!policyId}
            {!testReturn}
            <p><apex:outputText ><apex:param value="{!testReturn}" /></apex:outputText></p>
        </div>
    </body>
</html>
</apex:component>

Controller:

public class ClaimsListController2 {
    
    public Id policyId {get;set {loadList();}}
    public List<Case> claims{get;set;}
    public Contract policy {get;set;}
    public String testReturn {get;set;}
    public String testReturn2 {get;set;}
    
    public ClaimsListController2() {
        
        // this.policy = (Contract)stdController.getRecord();
        claims = [SELECT Id, CaseNumber, recordTypeId, Claim_Form_Recevied_Date__c, Policy__c, Reason, Claim_Cost__c, Status FROM Case
                  WHERE RecordTypeId='012b0000000Kr2VAAS' AND Policy__r.Id= :policyId];
        
        
        testReturn2 = 'constructor has run';
    }
    
    public void loadList() {
        claims = [SELECT Id, CaseNumber, recordTypeId, Claim_Form_Recevied_Date__c, Policy__c, Reason, Claim_Cost__c, Status FROM Case
                  WHERE RecordTypeId='012b0000000Kr2VAAS' AND Policy__c= :policyId];
        testReturn = 'loadList has run';

    }
    
}

As you can probably see in the controller, I tried also calling the query separately to the constructor, but neither really worked until I take out the limitation of the policy.

Best Answer

To start with

<c:StatementofClaims Contract="{!relatedTo.Id}" policyref="{!relatedTo.Id}" />
        

doesn't agree with the attribute definitions in the component:

 <apex:attribute name="Contract" description="Statement of Claims" type="Contract" />
 <apex:attribute name="policyref" description="" type="id" assignTo="{!policyId}" />

You are passing for attribute Contract the value of {!relatedTo.Id} which is of type ID but the component is expecting type Contract

You need to do:

<c:StatementofClaims Contract="{!relatedTo}" policyref="{!relatedTo.Id}" />

But, you've made this harder than it has to be:

The preferred pattern is as follows:

<c:StatementofClaims  policyref="{!relatedTo.Id}" />

The VF component has a single attribute

<apex:component layout="none" access="global" controller="ClaimsListController2">
<apex:attribute name="policyref" description="" type="id" assignTo="{!policyId}" />
<html>
...

And the VF component controller looks like this

public class ClaimsListController2 {
    
    public Id policyId {get;set;}
    public List<Case> claims{

      get {
         if (claims == null) {
            claims = this.policyId == null
               ? new List<Case>()
               : [SELECT Id, CaseNumber, ...
                   FROM Case WHERE
                        RecordtypeId = '012b0000000Kr2VAAS' AND
                        Policy__c = :this.policyId];
         return claims;
      } private set;
    }
    
    public ClaimsListController2() {}

}

Now, you should also avoid hardcoding recordtypeIds in Apex - use the Schema.DescribeSObjectResult.getRecordTypeInfosByDeveloperName(). See example

Related Topic