
Sometimes Salesforce customer/user needs to send a Contract to their client and they don't want to make the Contract Terms document (pdf) manually by their employee. The Salesforce provides the standard object called Contract, which is related with Account and this Contract object keeps all the information about the Contract terms. The customer/user wants to send an email to their client with Contracts records as an attached PDF document. In that case, we need to make the custom solution for this.
I am going to break this requirement into two part.
- The visualforce page which will show Contract details and it will render as PDF.
- The Lightning Component which will provide the user interface to enter required email fields.
So, Let's jump into the 1st part of the requirement. The visualforce page will show the details of Contract.
ContractPDF.vfp
<apex:page controller="ContractPDFCntrl" sidebar="false" showHeader="false" applyBodyTag="false" renderAs="PDF">
<html>
<head>
</head>
<body>
<table style="border-collapse: collapse; ">
<apex:repeat value="{!wrapperList}" var="row">
<tr style="border: 1px solid rgb(221, 219, 218); width:700px;">
<apex:repeat value="{!row.values}" var="value">
<td style="border: 1px solid rgb(221, 219, 218); padding: 15px; ">{!value}</td>
<td style="border: 1px solid rgb(221, 219, 218); padding: 15px;">
{!row.values[value]}
</td>
</apex:repeat>
</tr>
</apex:repeat>
</table>
</body>
</html>
</apex:page>
renderAs="PDF"
The renderAs="PDF" is an attribute of <apex:page> which is responsible for rendring the Visualforce page as pdf
Apex Controller: ContractPDFCntrl.apxc
public class ContractPDFCntrl {
//WrapperList
public List<Wrapper> wrapperList{get;set;}
public ContractPDFCntrl(){
wrapperList = new List<Wrapper>();
// getting Contract record Id from page URL
string recordId = ApexPages.CurrentPage().getParameters().get('id');
Contract cont = [select id,Name,BillingAddress,Account.Name,CompanySignedId,CompanySignedDate,EndDate,ContractNumber,StartDate,
ContractTerm,CustomerSignedDate, CustomerSignedTitle,Description,ShippingAddress,SpecialTerms,
Status from Contract where id=:recordId] ;
for (Integer idx=0; idx<15; idx++)
{
wrapperList.add(new Wrapper());
}
if(cont.Name!=null){
wrapperList[0].addValue('Name',cont.Name);
}
else{
wrapperList[0].addValue('Name','N/A');
}
if(cont.BillingAddress!=null){
Address addr = cont.BillingAddress;
wrapperList[1].addValue('Billing Street',addr.getStreet());
}else{
wrapperList[1].addValue('Billing Street','N/A');
}
if(Account.Name!=null ){
wrapperList[2].addValue('Name',cont.Account.Name);
}else{
wrapperList[2].addValue('Name','N/A');
}
if(cont.CompanySignedId !=null){
wrapperList[3].addValue('Company Signed Id',cont.CompanySignedId);
}else{
wrapperList[3].addValue('Company Signed Id','N/A');
}
if(cont.CompanySignedDate !=null){
wrapperList[4].addValue('Company SignedDate',String.valueOf(cont.CompanySignedDate));
}else{
wrapperList[4].addValue('Company SignedDate','N/A');
}
if(cont.EndDate !=null){
wrapperList[5].addValue('EndDate',String.valueOf(cont.EndDate));
}else{
wrapperList[5].addValue('EndDate','N/A');
}
if(cont.ContractNumber !=null){
wrapperList[6].addValue('Contract Number',String.valueOf(cont.ContractNumber));
}else{
wrapperList[6].addValue('Contract Number','N/A');
}
if(cont.StartDate !=null){
wrapperList[7].addValue('StartDate',String.valueOf(cont.StartDate));
}else{
wrapperList[7].addValue('StartDate','N/A');
}
if(cont.ContractTerm !=null){
wrapperList[8].addValue('ContractTerm',String.valueOf(cont.ContractTerm));
}else{
wrapperList[8].addValue('ContractTerm','N/A');
}
if(cont.CustomerSignedDate !=null){
wrapperList[9].addValue('CustomerSignedDate',String.valueOf(cont.CustomerSignedDate));
}else{
wrapperList[9].addValue('CustomerSignedDate','N/A');
}
if(cont.CustomerSignedTitle !=null){
wrapperList[10].addValue('CustomerSignedTitle',String.valueOf(cont.CustomerSignedTitle));
}else{
wrapperList[10].addValue('CustomerSignedTitle','N/A');
}
if(cont.Description !=null){
wrapperList[11].addValue('Description',String.valueOf(cont.Description));
}else{
wrapperList[11].addValue('Description','N/A');
}
if(cont.ShippingAddress !=null){
Address addr = cont.ShippingAddress;
wrapperList[12].addValue('ShippingAddress',String.valueOf(addr.getStreet()));
}else{
wrapperList[12].addValue('ShippingAddress','N/A');
}
if(cont.SpecialTerms !=null){
wrapperList[13].addValue('SpecialTerms',String.valueOf(cont.SpecialTerms));
}else{
wrapperList[13].addValue('SpecialTerms','N/A');
}
if(cont.Status !=null){
wrapperList[14].addValue('Status',String.valueOf(cont.Status));
}else{
wrapperList[14].addValue('Status','N/A');
}
}
//Wrapper class
public class Wrapper{
public Map<String,string> values {get; set;}
// constructor
public Wrapper()
{
values = new Map<String,String>();
}
//Wrapper method
public void addValue(String Name, String Value)
{ //put data in values map.
values.put(Name,Value);
}
}
}
When you preview the page it will look like the screen below.
https://yourdomain-dev-ed--c.ap7.visual.force.com/apex/ContractPDF?id=ContractId
2nd Part: Lightning Component.
SendEmailComponent.cmp
<aura:component controller="SendEmailCntrl" implements="force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId,forceCommunity:availableForAllPageTypes,force:lightningQuickAction" access="global" >
<aura:handler name="init" value="{!this}" action="{!c.OnLoad}"/>
<!-- attributes-->
<aura:attribute name="email" type="string"/>
<aura:attribute name="subject" type="string"/>
<aura:attribute name="body" type="string"/>
<aura:attribute name="cc" type="string"/>
<aura:attribute name="Bcc" type="string"/>
<aura:attribute name="mailStatus" type="boolean" default="false"/>
<!-- Account record Id attaribute -->
<aura:attribute name="recordId" type="string"/>
<!-- Wrapper -->
<aura:attribute name="WrapObj" type="wrapper"/>
<!-- User Info -->
<aura:attribute name="currentUser" type="user"/>
<!-- CC input TRUE?FALSE Boolean -->
<aura:attribute name="displayCC" type="Boolean" default="false"/>
<div class="slds-m-around--medium">
<div class="slds-container--medium">
<div class="slds-form--stacked">
<div class="slds-form-element">
<label class="slds-form-element__label" for="CC">From</label>
<div class="slds-form-element__control">
<ui:inputEmail class="slds-input" aura:id="email" value="{!v.currentUser.Email}" required="true" placeholder="abc@email.com" disabled="true"/>
</div>
</div>
<div class="slds-form-element">
<label class="slds-form-element__label" for="CC">To</label>
<div class="slds-form-element__control">
<ui:inputEmail class="slds-input" aura:id="email" value="{!v.email}" required="true" placeholder="abc@email.com"/>
<a onclick="{!c.showCC}">CC</a>
</div>
</div>
<aura:if isTrue="{!v.displayCC}">
<div class="slds-form-element">
<label class="slds-form-element__label" for="CC">Cc</label>
<div class="slds-form-element__control">
<ui:inputEmail class="slds-input" aura:id="cc" value="{!v.cc}" placeholder="abc@email.com"/>
</div>
</div>
</aura:if>
<div class="slds-form-element">
<label class="slds-form-element__label" for="CC">Bcc</label>
<div class="slds-form-element__control">
<ui:inputEmail class="slds-input" aura:id="Bcc" value="{!v.Bcc}" placeholder="abc@email.com"/>
</div>
</div>
<div class="slds-form-element">
<label class="slds-form-element__label" for="CC">Subject</label>
<div class="slds-form-element__control">
<ui:inputText class="slds-input" aura:id="subject" value="{!v.subject}" placeholder="Subject"/>
</div>
</div>
<div class="slds-form-element">
<label class="slds-form-element__label" for="textareaSample2">Mail Body</label>
<div class="slds-form-element__control">
<lightning:inputRichText aura:id="body" value="{!v.body}" />
</div>
</div>
<div class="slds-page-header">
Attach Contract
</div>
<table class="slds-table slds-table--bordered" style="border-right: 1px solid rgb(216, 221, 230); border-left: 1px solid rgb(216, 221, 230);border-top: 0px solid rgb(216, 221, 230);">
<thead>
<tr>
<th>Select</th>
<th>Contract Number</th>
</tr>
</thead>
<tbody>
<aura:iteration items="{!v.WrapObj}" var="w">
<tr>
<td><ui:inputCheckbox class="myCheckbox" aura:id="check" value="{!w.check}"/></td>
<td>{!w.contrct.ContractNumber}</td>
</tr>
</aura:iteration>
</tbody>
</table>
<br/><br/>
<button type="button" class="slds-button slds-button--brand" onclick="{!c.sendMail}">Send</button>
</div>
</div>
</div>
<!-- End -->
</aura:component>
SendEmailComponentController.js ({
OnLoad : function(component,event,helper){
helper.getCurrentUser(component,event,helper);
var action = component.get("c.onLoad");
action.setParams({
"recordId" : component.get("v.recordId")
});
action.setCallback(this,function(response){
var state = response.getState();
if(state == 'SUCCESS'){
var result = response.getReturnValue();
console.log('Result : ### ' + JSON.stringify(result));
component.set("v.WrapObj", result);
}else{
console.log('something bad happed! ');
}
});
$A.enqueueAction(action);
},
sendMail: function(component, event, helper) {
//alert('**** Test');
var wrappers = component.get('v.WrapObj');
var ids=new Array();
alert('array size: ' +wrappers.length);
for (var idx=0; idx<wrappers.length; idx++) {
alert(wrappers[idx].check);
if (wrappers[idx].check) {
ids.push(wrappers[idx].contrct.Id);
}
}
var idListJSON=JSON.stringify(ids);
//alert(idListJSON);
var Email = component.get("v.email");
var Subject = component.get("v.subject");
var body = component.get("v.body");
var CcEmail = component.get("v.cc");
var BcccEmail = component.get("v.Bcc");
if ($A.util.isEmpty(Email) || !Email.includes("@")) {
alert('Please Enter valid Email Address');
} else {
helper.sendEmailHelper(component, Email, Subject, body, idListJSON,CcEmail,BcccEmail);
}
},
//Show CC email input
showCC : function(component,event,helper){
component.set("v.displayCC", true);
},
closeModal : function(component,event,helper){
//Close the modal
component.set("v.isOpen",false);
},
})
SendEmailComponentHelper.js ({
sendEmailHelper: function(component, getEmail, getSubject, getbody,getContractIds,getCcEmail,getBcccEmail) {
// call the server side controller method to send email
var action = component.get("c.sendContractMail");
action.setParams({
'emailId': getEmail,
'subject': getSubject,
'emailbody': getbody,
'ContractIds' :getContractIds,
'ccEmail' : getCcEmail,
'bccEmail' : getBcccEmail
});
action.setCallback(this, function(response) {
var state = response.getState();
if (state === "SUCCESS") {
var storeResponse = response.getReturnValue();
//If server response return 'success' show toast message that email has been sent.
if(storeResponse == 'success'){
var toastEvent = $A.get("e.force:showToast");
toastEvent.setParams({
"title": "Success!",
"type": "success",
"message": "Email Sent Successfully!"
});
toastEvent.fire();
var navEvt = $A.get("e.force:navigateToSObject");
navEvt.setParams({
"recordId": component.get("v.recordId"),
"slideDevName": "detail"
});
navEvt.fire();
}
else{ // show error toast message
var toastEvent = $A.get("e.force:showToast");
toastEvent.setParams({
"title": "Success!",
"type": "error",
"message": storeResponse
});
toastEvent.fire();
}
}
});
// calling server side Apex method(This will be called asynchronously).
$A.enqueueAction(action);
},
getCurrentUser : function(component,event,helper){
var action = component.get("c.fetchUserDetails");
action.setCallback(this,function(response){
var state = response.getState();
if(state == 'SUCCESS'){
var result = response.getReturnValue();
console.log('result: ' + result);
component.set("v.currentUser", result);
}
else{
console.log('Something bad happend! ');
}
});
// calling sever side action method
$A.enqueueAction(action);
}
})
Apex Controller: public class SendEmailCntrl {
@AuraEnabled
public static List<Wrapper> onLoad(string recordId){
List<Wrapper> wrapList = new List<Wrapper>();
Account acc = [select id,(select id,Name,ContractNumber from Contracts) from Account where id=:recordId];
for(Contract c: acc.contracts){
wrapList.add(new Wrapper(false,c));
}
//Return wrapperList
return wrapList;
}
@AuraEnabled
public static user fetchUserDetails(){
// query current user information
User oUser = [select id,Name,TimeZoneSidKey,Username,Alias,Country,Email,FirstName,LastName,IsActive
FROM User Where id =: userInfo.getUserId()];
return oUser;
}
@AuraEnabled
public static String sendContractMail(String emailId ,String subject ,String emailbody,String ContractIds, String ccEmail, String bccEmail){
system.debug('ContractIds: ' +ContractIds);
String SUCCESS_MSG = '';
String ERROR_MSG = '';
List<Messaging.SingleEmailMessage> mails = new List<Messaging.SingleEmailMessage>();
// Create mail obj
Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
// list of people to whome email will be send
List<String> sendTo = emailId.split(',');
//sendTo.add(mMail);
List<String> ccTo = new List<String>();
if(ccEmail!='' && ccEmail!=null){
ccTo = ccEmail.split(',');
}
List<String> bccTo = new List<String>();
if(bccEmail!='' && bccEmail!=null){
bccTo = bccEmail.split(',');
}
// Send To
mail.setToAddresses(sendTo);
// bcc to
if(bccTo.size()>0)
mail.setBccAddresses(bccTo);
//cc to
if(ccTo.size()>0)
mail.setCcAddresses(ccTo);
//Set who the email is sent from
mail.setReplyTo('noreply@gmail.com'); // change it with your mail address.
mail.setSenderDisplayName('salesforce User');
// subject and email body
mail.setSubject(subject);
mail.setHtmlBody(emailbody);
// add email in list
mails.add(mail);
Type idArrType=Type.forName('List<Id>');
// selected contarctIds
List<Id> ids=(List<Id>) JSON.deserialize(ContractIds, idArrType);
//List of Emailfileattachment
List<Messaging.Emailfileattachment> fileAttachments = new List<Messaging.Emailfileattachment>();
// Map<Contract, Blob>
Map<Contract, Blob> bodyMap = new Map<Contract, Blob>();
//Attachments
for(Contract c: [select id,ContractNumber from Contract where id IN: ids]){
PageReference pdf = Page.ContractPDF;//ContractPDF is the name of vf page
pdf.getParameters().put('id',c.Id); //sending contract id paramater to 'ContractPDF' VF.
bodyMap.put(c, pdf.getContent()); // get vf page as blob.
}
for(Contract cntrct: bodyMap.keySet()){
// Add to attachment file list
Messaging.Emailfileattachment efa = new Messaging.Emailfileattachment();
efa.setFileName(cntrct.ContractNumber);
if(bodyMap.containsKey(cntrct)){
Blob b = bodyMap.get(cntrct);
efa.setBody(b);
}
efa.setContentType('application/pdf');
fileAttachments.add(efa);
}
mail.setFileAttachments(fileAttachments);
//Send email
try{
Messaging.sendEmail(mails);
SUCCESS_MSG = 'success';
return SUCCESS_MSG;
}
catch(Exception e){
ERROR_MSG = 'Error:'+e.getmessage();
return ERROR_MSG;
}
}
}
Now create Quick Action button on Account.Add this Quick Action on Account page Layout.
Output:
Hope this post will help many.
Thanks
Arun Kumar
Hi, I am receiving error in SendEmailCntrl
ReplyDeleteError : Constructor not defined: [Wrapper].(Boolean, Contract)
for this part of code:
for(Contract c: acc.contracts){
wrapList.add(new Wrapper(false,c)); //error
}
Thanks a lot very much for the high quality and results-oriented help. I won’t think twice to endorse your blog post to anybody who wants and needs support about this area.
ReplyDeleteCRM with Invoicing