Author: Ravi Kumar Rai & Mohanraj D
Summary:
The Invoice Automation is a custom functionality, which will help us to create Invoices from Quotes without third party applications and share the same with customer using Email and attach the document to Related Attachment object.This Invoice generator button is actually a Quick Action button which enables you to create your own Invoice using customized PDF Template apart from the Template provided by Salesforce itself.
Assumptions:
The following are the assumptions, while implementing the Invoice generator functionality,
- Primary Quote should be generated and synced with Opportunity
- Invoice generator is triggered from Order record page ( considering Order creation as an immediate process once Quote is accepted by customer)
Setup:
Let’s Suppose we want to send Invoice email to the customer once Order is being created and Prospect is converted. We can Create a Quick Action button which will be discussed later.
We need to Setup the Classic Email Template ( Visualforce email ) in salesforce org , which contains the letterhead subject and email body according to the requirement. Let’s look into the process of creating Email Template:
1. Go to Classic Email Template and Select “ Visualforce” .
2. Fill in all the Fields based on your need

3. As you can See in the above snap we have filled in details , now save and click on Edit Template , create your own body accordingly. For instance refer below snap.

4. As you can see the recipientType is set as contact as here the email is intended to be sent to the related contact only. You can access any contact related field using {!recipient.FieldAPiName} as shown above.
Now we can use flow and Apex to integrate our email template , also create a visual force page to create a PDF template and use this template in our apex while sending email as an attachment.
Flow (On Order Object):
1. Go to flows in setup , select screen flow and configure your flow as shown below , we need to send various details required to be sent to Apex to create email template and send mail to prescribed and authenticated users..
2. First we need to Select Get Records and get the recordId of the record.


3. Now moving on with the flow , we can put a decision block to check for the valid contact for that associated Quote
4. If Contact is available , then we can capture that email Id in a readonly textbox and display it for confirmation purposes.
5. Use assignment block to assign values which needed to be passed while calling Apex.create a Collection Variable for this in our Case let’s name it colvar.
( colvar contains Email Id from associated contact , Related Opportunity Id, Order Id(recordId), Contact Id)


6. After that it’s time to call Our Apex class using Apex action with the parameters as shown..
7. But before that lets create a Quick Action Button on Order Object.
- Go to order Object in Object manager and click on Button , Links and Actions
- Click on Quick Action Button on top right corner
- Name your Quick Action like “Invoice Generator” and Select Flow with the flow name which you provided at the time of flow creation.
- Add this button On Page Layout using the Edit Page option in Order.
8. Now we are done with Template as well as Flow , Let’s move on to Apex to know the process and code needed to achieve it.
Flow invoked Apex:
public class InvoicePDF {
@InvocableMethod(label=’email invoice pdf’) // get all the values passed from flow and parse it
public static void testmail(List<List<String>> args) {
List<String> nested=args[0];
String email=nested[0];
String recordId=nested[1];
String orderId = nested[2];
String conId=nested[3];
createpdf(email,recordId,orderId,conId);
}
@future(Callout=true)
public static void createpdf(String email , String recordId,String orderId,String conId)
{
Quote theQuote;
opportunity ops = [SELECT Id,
(SELECT Id,
Name,
IsSyncing,
CreatedDate
FROM Quotes
WHERE IsSyncing = true
AND Status=’Accepted’
ORDER BY CreatedDate DESC)
FROM Opportunity
WHERE Id =: recordId];
if(ops.Quotes.isEmpty()) { //Checking for Synced quote
theQuote = null;
}
else {
theQuote = ops.Quotes;
PageReference pageRef = new PageReference(‘/apex/InvoiceTemplate?oppId=’+recordId); // Creating Instance for Invoice template (Visualforce Page)
Blob pdfbody;
if(Test.isRunningTest())
pdfbody=blob.valueOf(‘Unit Test’);
else
pdfbody=pageRef.getContent();
List<Attachment> attList = new List<Attachment>();
attList.add( new Attachment ( //Attaching it to related list
Name = ‘Invoice #’ + theQuote.Name + ‘.pdf’,
Body = pdfbody,
ParentId = ops.Id
));
attList.add( new Attachment (
Name = ‘Invoice #’ + theQuote.Name + ‘.pdf’,
Body = pdfbody,
ParentId = orderId
));
if (!attList.isEmpty()) {
System.debug(‘attList’+attList);
Database.SaveResult[] srList = Database.insert(attList, false);
for (Database.SaveResult sr : srList) {
if (sr.isSuccess()) {
system.debug(‘Successfully inserted’);
}
else
{
for(Database.Error err : sr.getErrors()) {
System.debug(‘Error attachment’+err.getStatusCode() + ‘: ‘ + err.getMessage());
System.debug(‘Account fields that affected this error: ‘ + err.getFields());
}
}
}
}
EmailTemplate et=[SELECT Id,
Subject
FROM EmailTemplate
WHERE Name = ‘Invoice Classic Template’];
Messaging.EmailFileAttachment attachment=new Messaging.EmailFileAttachment();
attachment.setContentType(‘application/pdf’);
attachment.setFileName(‘e-Invoice.pdf’);
attachment.body=pdfbody;
attachment.setInline(false);
Messaging.SingleEmailMessage mail=new Messaging.SingleEmailMessage();
mail.setToAddresses(new String[] {email});
mail.setTemplateId(et.Id);
mail.setTargetObjectId(conId);
mail.setSaveAsActivity(false);
mail.setWhatId(conId);
mail.setBccSender(false);
mail.setUseSignature(false);
mail.setFileAttachments(new Messaging.EmailFileAttachment[] {attachment});
List<Messaging.SendEmailResult> results = Messaging.sendEmail(new Messaging.SingleEmailMessage[] {mail});
if (!results.get(0).isSuccess()) {
System.StatusCode statusCode = results.get(0).getErrors()[0].getStatusCode();
String errorMessage = results.get(0).getErrors()[0].getMessage();
}
}
}
}
VisualForce Page Template:
<apex:page controller=”InvoicePDFController” applyHtmlTag=”false” showHeader=”false” renderAs=”PDF”>
<html>
<head></head>
<body >
<table style=”font-family:sans-serif; padding-bottom:30px;width:100%”>
<tr>
<td><apex:image id=”logo2″ value=”{!$Resource.ApimotocorpLogo}” width=”180″ height=”90″/></td>
<td style=”font:22pt; padding-left:10px; padding-right:140px;”>XYZ Corporation</td>
<td style=”font:22pt;”>INVOICE</td>
</tr>
</table>
<br></br>
<table style=”font-family:sans-serif;font-size:12px;width:100%”>
<tr style=”color:#fff; background-color: rgb(3, 175, 243)”>
<th style=”padding-left:5px; padding-right:170px;”>Bill To</th>
</tr>
<tr>
<td style=”padding-left:5px;width:35%;height:30px”><apex:outputField value=”{!quoteobj.BillingName}”/></td>
<td style=”width:40%;text-align:right”>Created Date</td>
<td style=”padding-left:5px;”>{!dt}</td>
</tr>
<tr>
<td style=”padding-left:5px;height:30px”><apex:outputField value=”{!quoteobj.BillingStreet}”/></td>
<td style=”width:40%;text-align:right”>Contact Name</td>
<td style=”padding-left:5px;”>{!quoteobj.contact.Name}</td>
</tr>
<tr>
<td style=”padding-left:5px;height:30px”><apex:outputField value=”{!quoteobj.BillingState}”/></td>
<td style=”width:40%;text-align:right”>Email</td>
<td style=”padding-left:5px;”>{!quoteobj.contact.Email}</td>
</tr>
<tr>
<td style=”padding-left:5px;height:30px”><apex:outputField value=”{!quoteobj.BillingCountry}”/></td>
<td style=”width:40%;text-align:right”>Phone</td>
<td style=”padding-left:5px;”>{!quoteobj.contact.phone}</td>
</tr>
<tr>
<td style=”padding-left:5px;height:30px”><apex:outputField value=”{!quoteobj.BillingPostalCode}”/></td>
</tr>
</table>
<br></br>
<table style=”font-family:sans-serif;width:100%;font-size:12px;” cellpadding=”0″ cellspacing=”0″>
<tr style=”color:#fff;background-color: rgb(3, 175, 243);”>
<th style=”text-align:center;”>Description</th>
<th style=”text-align:right;padding-right:5px;”>Qty</th>
<th style=”text-align:right;padding-right:5px;”>Unit Price</th>
<th style=”text-align:right;padding-right:5px;”>Amount</th>
</tr>
<apex:repeat value=”{!quoteobj.quoteLineItems}” var=”lineItem”>
<tr style=”background-color:#ffdecc;”>
<td style=”height:30px;text-align:center;”>{!lineItem.product2.Name}</td>
<td style=”text-align:right;padding-right:5px;”>{!lineItem.quantity}</td>
<td style=”text-align:right;padding-right:5px;”>{!lineItem.unitprice}</td>
<td style=”text-align:right;padding-right:5px;”>{!lineItem.subtotal}</td>
</tr>
</apex:repeat>
<tr>
<td style=”height:30px;” ></td>
<td ></td>
<td style=”text-align:right;padding-right:5px;”>Subtotal</td>
<td style=”text-align:right;padding-right:5px;”>{!quoteobj.Subtotal}</td>
</tr>
<tr>
<td style=”height:30px;text-align:center”> Prepared by</td>
<td style=”text-align:left”>{!userName}</td>
<td style=”text-align:right;padding-right:5px;”>Discounts</td>
<td style=”text-align:right;padding-right:5px;”>{!quoteobj.discount}</td>
</tr>
<tr>
<td style=”height:30px;text-align:center”>Email</td>
<td style=”text-align:left”>{!$User.Email}</td>
<td style=”text-align:right;padding-right:5px;”>Taxes</td>
<td style=”text-align:right;padding-right:5px;”>{!quoteobj.tax}</td>
</tr>
<tr>
<td style=”height:30px”></td>
<td ></td>
<td style=”text-align:right;padding-right:5px;”>Shipping charge</td>
<td style=”text-align:right;padding-right:5px;”>{!quoteobj.shippinghandling}</td>
</tr>
<tr>
<td style=”height:30px;”></td>
<td ></td>
<td style=”text-align:right;padding-right:5px;background-color: rgb(3, 175, 243);”>Total</td>
<td style=”text-align:right;padding-right:5px;background-color: rgb(3, 175, 243);”>{!quoteobj.Grandtotal}</td>
</tr>
</table>
<br></br>
<br></br>
<table style=”width:100%; font-family:sans-serif;font-size:12px”>
<tr>
<td style=”text-align:center”> If you have any questions about this invoice , Please contact support@xyzcor.com </td>
</tr>
<tr>
<td style=”text-align:center”>Thank you for your business!</td>
</tr>
</table>
</body>
</html>
</apex:page>
VisualForce Page Custom controller:
public class InvoicePDFController {
public Opportunity opp {get; set;}
public String oppId {get;set;}
public Quote quoteobj {get;set;}
public string userName {get;set;}
public String dt {get;set;}
public InvoicePDFController(){
this.oppId = apexpages.currentpage().getparameters().get(‘oppId’);
if(this.oppId!=null) {
this.opp = [SELECT Id
FROM Opportunity
WHERE Id =: this.oppID];
this.quoteobj = [SELECT Id,
Name,
contact.Name,
contact.phone,
contact.Email,
IsSyncing,
BillingName,
BillingStreet,
BillingCity,
BillingState,
BillingPostalCode,
BillingCountry,
CreatedDate,
Subtotal,
Tax,
Discount,
Shippinghandling,
Totalprice,
Grandtotal,
(SELECT product2.Name,
Quantity,
Unitprice,
Subtotal,
total price
FROM quotelineItems)
FROM Quote
WHERE IsSyncing = true
AND opportunityId = :this.opp.Id
ORDER BY CreatedDate
DESC ];
this.userName=userinfo.getName();
this.dt= date.today().format();
}
}
}
Explanation:
- We are parsing data passed from flow and passing to createpdf() method.
- Then checking for Synced quotes to process further.
- Creating Instance for VisualForce page template & Passing Opportunity record Id as url parameter (Refer: Invoice Template & InvoicePDF controller ).
- Capturing the rendered Visualforce Page as PDF and using it for Mail attachment.
- Then adding that PDF to Attachments Object related to Order andOpportunity.
- Then creating Instance for SingleEmailMessage and passing necessary parameters (setTargetObjectId(contactId),fileName() , contentType(). etc).
- And sending email using sendEmail() method with Invoice PDF attachment
Sample Template:
