##Custom Invoice Templates
Last Updated: 9/14/15
####Goal After following this guide, you should be able to create custom invoice templates by leveraging the custom invoice components and controllers, along with CSS.
####Prerequisites The ability to create custom templates assumes the following:
- Version 3.376+ of the Apto software
- System Administrator access
- You are familiar with creation and manipulation of:
- Visualforce Pages
- Visualforce Components
- APEX Classes
- CSS3
####Building Blocks
#####Visualforce Pages
-
InvoicePreview.page
This page provides an example of the custom components and controllers can be combined to create flexible invoice layouts with sections shown or hidden based on a variety of invoice properties.
#####Visualforce Components
The following components serve as reusable building blocks that can be added to an invoice page in order to display a variety of information. All components require an invoice_id
attribute to be set. The other attributes available are listed for each component in their respective sections.
Important Note! Many components have the attribute fields
that can either accept field sets that have been defined on the Invoice object in SalesForce, or it can accept a comma-delimited string containing invoice field names. If we opt to use comma-delimited strings for the field names, then we as developers are responsible for prepending any API names with an appropriate namespace so that the controllers are able to query the correct fields.
-
#####InvoiceHeader.component
Description
This component provides the layout for the header section of an invoice. It is currently configured to display a company logo on the right side of the header and a company address on the left side of the header. This positioning can be overridden with CSS, and the address lines (populated by
fields
) can substituted with other lines of data or omitted.Its parent CSS class is
header
. Each line of data has the CSS classdata-line
as well asdata-line-#
where '#' begins at 1 and increments by 1 for each line.Attributes
Name Required Description fields no A field set or comma-delimited string of invoice fields to display on the header.
-
#####InvoiceAddress.component
Description
This component provides the layout for addresses in the invoice.
Its parent CSS class is
address
. Each line of data has the CSS classaddress-line
as well asaddress-line-#
where '#' begins at 1 and increments by 1 for each line.Attributes
Name Required Description title no A title to display above the address. fields yes A field set or comma-delimited string of address fields.
-
#####InvoiceDetails.component
Description
This component provides the layout for invoice details.
Its parent CSS class is
invoice-details
. Each line of data has the CSS classdetails-line
as well asdetails-line-#
where '#' begins at 1 and increments by 1 for each line.Attributes
Name Required Description title no A title to display above the address. field_labels yes A comma-delimited string of labels to associated with each field set member. fields yes A field set or comma-delimited string of invoice detail fields. Note:
field_labels
andfields
are expected to resolve to arrays of the same length.
-
#####InvoiceLineItems.component
Description
This component provides a table layout for invoice line items.
Its parent CSS class is
line-items
.Attributes
Name Required Description title no A title to display above the address. field_labels yes A comma-delimited string of labels to associated with each field set member. fields no A field set or comma-delimited string of invoice line item fields. If this field is not set, data values for each row are derived based on a known set of labels. See the InvoiceLineItemsController.cls description for more details. total_label yes The label to display next to the total calculated for the line items. Note: If both are set,
field_labels
andfields
are expected to resolve to arrays of the same length.
-
#####InvoiceBillingSchedule.component
Description
This component provides a table layout for the invoice billing schedule.
Its parent CSS class is
billing-schedule
.Attributes
Name Required Description title no A title to display above the address. field_labels yes A comma-delimited string of labels to associated with each field set member. fields no A field set or comma-delimited string of invoice billing schedule fields. If this field is not set, data values for each row are derived based on a known set of labels. See the InvoiceBillingScheduleController.cls description for more details. Note: If both are set,
field_labels
andfields
are expected to resolve to arrays of the same length.
#####APEX Classes The following classes provide the fountain for controlling the data that is provided to the Visualforce pages and components.
-
#####InvoiceComponentController.cls
This class is the controller for the Custom Invoice Page. It's primary purpose is to set up a variety of Boolean values based on the characteristics of a given invoice in order to help determine which components should be displayed on the page, and which values should be used to initialize those components.
The following properties can be accessed by the Visualforce page:
Name Type Description invoiceId Id The Id of the invoice to use to populate the invoice template. When this value is passed into the controller by a component, the rest of the properties below are initialized. invoice Invoice__c The invoice to use to populate the invoice template. commissionItemQuantityLabel String The column header used in the line items section for lease commission item quantities. commissionItemAmountLabel String The column header used in the line items section for lease commission item amounts. isRecurringInvoice Boolean True if the invoice type is 'Over the Term'. isSaleRecordType Boolean True if the invoice is for a sale. hasLeaseCommissionItems Boolean True if the invoice has lease commission items. hasNonLeaseCommissionItems Boolean True if the invoice has non-lease commission items. hasSaleCommissionItems Boolean True if the invoice has sale commission items. hasInvoiceList Boolean True if there is a list of invoices associated with a given invoice comp. hasRecurringInvoiceList Boolean True if there is a list of unpaid 'Over the Term' invoices associated with a given invoice comp.
-
#####InvoiceController.cls
This is a base class that provides a common set of properties and methods for other controllers to use. The most important properties are
fieldSetLabels
andfieldSetMembers
, which provide arrays of values to be used by the components. These properties are discussed in greater details in the Visualforce Components section. Simple components which require no special handling, such as the InvoiceDetails component, can use this class as their controller. -
#####InvoiceHeaderController.cls
This class inherits from the InvoiceController. In additional to the default properties which can be used to display address lines or other information in the header, it also provides a
companyLogoUrl
property derived from theInvoice_Template__c
of the invoice, which allows a company logo to be displayed in the header. -
#####InvoiceAddressController.cls
This class inherits from the InvoiceController. It contains additional logic to allow for better display of addresses by combining lines for City, State, and Zip into a single line.
-
#####InvoiceTableController.cls
This is an abstract class which inherits from the InvoiceController and provides additional properties and methods to support table-based layouts, such as those used by the InvoiceLineItems and the InvoiceBillingSchedule components. It also provides a
total
property which can be used to sum a field in each row. -
#####InvoiceLineItemsController.cls
This class provides the logic for displaying lease or sale commission line items. The value of
total
is the sum of theCommission_Amount__c
for each commission line item. This class can accept bothfieldSetLabels
andfieldSetMembers
, however, if nofieldSetMembers
are provided, it will calculate line item values based on a set of recognized system labels. The recognized labels are:Label.Line_Item Label.Type Label.Term Label.Area_Sq_Ft Label.Years Label.Months Label.DollarSign_SF_Year Label.DollarSign_SF_Month Label.Dollar_Year Label.Month Label.Amount Label.Commission Label.Commission_DollarSign
-
#####InvoiceBillingScheduleController.cls
This class provides the logic for displaying billing schedules. It will add a Due Now label to the set of labels it receives. The value of Due Now is calculated for each row based on the difference between the
Total_Due__c
andAmount_Paid__c
. The value oftotal
is the sum of each of those calculations. This class can accept bothfieldSetLabels
andfieldSetMembers
, however, if nofieldSetMembers
are provided, it will calculate line item values based on a set of recognized system labels. The recognized labels are:Label.Invoice_Number Label.Term Label.Rent_Month Label.Commission Label.Commission_DollarSign Label.Amount_Paid Label.Due_Date Label.SubTotal Label.Tax Label.Total
####Creating Your Own Custom Invoice Templates
Additional invoice templates can be created using the InvoicePreview page as a guide. Components can display different data by changing their field_labels
and fields
attributes. The invoice layout can be altered by modifying the DOM surrounding the various components in the page, overriding the default CSS, or a combination of both.
#####Example
This example shows how to use the components to create a basic invoice. Since no specific logic is required in this example, the page we create will not specify an extension controller or implement conditional logic to dynamically control which components are rendered. For a more complicated example, refer to CustomInvoicePreview.page and CustomInvoicePreview.component.
-
Step 1: Navigate to Build > Develop > Components, and create an new component called MyCustomInvoice. Refer to Creating and Using Custom Components for additional details on working with components. Copy and paste the boilerplate code below into your component. Note that we set the standard controller to
Invoice__c
and specify a stylesheet for the page to use. Also note that we setrenderA="pdf"
on the page so that our invoice is created as a PDF. It is sometimes helpful remove that attribute so that you can inspect the page elements and manipulate their layout with your browser developer tools in order to fine tune any custom CSS.<apex:component controller="McLabs2.InvoiceComponentController" access="global"> <apex:styleSheet value="{!URLFOR($Resource.McLabs2__AptoResources, 'css/invoice.css')}"/> <apex:attribute name="invoice_id" type="Id" description="the Id of the invoice" assignTo="{!invoiceId}"/> <body> <!-- We'll add our other components here --> </body> </apex:component>
-
Step 2: Now we'll add a header component to our page. The header component will automatically add a company logo to the invoice (based on the value of the invoice's
Invoice_Template__c
). It can also add lines of data, such as address lines, by setting thefields
using a field set or a comma-delimited string. We will use a field set calledInvoiceHeaderFields
that should already be available on the Invoice object. An administrator can change this fieldset, and the next time the invoice is generated the new field set values will be used.<body> <McLabs2:InvoiceHeader invoice_id="{!invoice.Id}" fields="{!$ObjectType.McLabs2__Invoice__c.FieldSets.McLabs2__InvoiceHeaderFields}"/> </body>
-
Step 3: Next we'll add a couple addresses to the top of the invoice. We'll nest these in some divs so that we can better control the layout. We'll also make use of
<apex:outputPanel>
to allow us to display different address components based on certain properties of the invoice. We'll apply this strategy numerous times through the remaining steps in order to end up with a more flexible solution.<body> ... <div class="top"> <div class="half address-section"> <div class="bill-to"> <!-- Bill To address to display if the Bill To field is empty or set to 'Recipient Account' --> <apex:outputPanel rendered="{!OR(invoice.Bill_To__c =='Recipient Account',invoice.Bill_To__c =='')}"> <McLabs2:InvoiceAddress invoice_id="{!invoice.Id}" fields="{!$ObjectType.McLabs2__Invoice__c.FieldSets.McLabs2__BillToRecipientAddressFields}"/> </apex:outputPanel> <!-- Bill To address to display for a Sale invoice with a Bill To of 'Property Ownership Entity' --> <apex:outputPanel rendered="{!AND(isSaleRecordType, invoice.Bill_To__c =='Property Ownership Entity')}"> <McLabs2:InvoiceAddress invoice_id="{!invoice.Id}" fields="Comp__r.Property__r.Ownership_Entity__c,Comp__r.Property__r.Ownership_Entity_Address__c"/> </apex:outputPanel> <!-- Bill To address to display for a Lease invoice with a Bill To of 'Property Ownership Entity' --> <apex:outputPanel rendered="{!AND(NOT(isSaleRecordType), invoice.Bill_To__c =='Property Ownership Entity')}"> <McLabs2:InvoiceAddress invoice_id="{!invoice.Id}" fields="Comp__r.Property_Leases__r.Ownership_Entity__c,Comp__r.Property_Leases__r.Ownership_Entity_Address__c"/> </apex:outputPanel> </div> <div class="leased-to"> <!-- Sold To address to display for a Sale invoice --> <apex:outputPanel rendered="{!isSaleRecordType}"> <McLabs2:InvoiceAddress title="{!$Label.McLabs2__Sold_to}" invoice_id="{!invoice.Id}" fields="Comp__r.Buyer_Company__r.Name,Comp__r.Property_Leases__r.Name,Comp__r.Property__r.Name,Comp__r.Property_Address__c,Comp__r.Property_City__c,Comp__r.Property_State__c,Comp__r.Property_Zip_Postal_Code__c,Comp__r.Property_Country__c"/> </apex:outputPanel> <!-- Sold To address to display for a Lease invoice --> <apex:outputPanel rendered="{!NOT(isSaleRecordType)}"> <McLabs2:InvoiceAddress title="{!$Label.McLabs2__Sold_to}" invoice_id="{!invoice.Id}" fields="Comp__r.Tenant__r.Name,Comp__r.Property_Leases__r.Name,Comp__r.Property__r.Name,Comp__r.Property_Address__c,Comp__r.Property_City__c,Comp__r.Property_State__c,Comp__r.Property_Zip_Postal_Code__c,Comp__r.Property_Country__c"/> </apex:outputPanel> </div> </div> </div> </body>
-
Step 4: Now we'll add some invoice details to display on the right-hand side of the page, next to the addresses. We'll include details for the invoice number, date, terms, and due date. We'll also specify some labels to show next to these fields. We'll add these inside of
<div class="top">
and use CSS classes to display the details to the right of the addresses.<body> ... <div class="top"> ... <div class="half invoice-details-section"> <McLabs2:InvoiceDetails title="Invoice" invoice_id="{!invoice.Id}" field_labels="{!$Label.McLabs2__Invoice_Number}, {!$Label.McLabs2__InvoiceDate}, {!$Label.McLabs2__Terms}, {!$Label.McLabs2__Invoice_Due_Date}" fields="{!$ObjectType.McLabs2__Invoice__c.FieldSets.McLabs2__InvoiceDetailFields}"/> </div> </div> </body>
-
Step 5: Below the addresses and invoice details we'll add a section for invoice line items. The InvoiceLineItems component can accept
fields
, but it also has a set of recognized labels that it can use to build the line items. Some of these labels, such asLabel.Term
, allow multiple fields to be combined into a single field in the line items table. Automatic merging and calculation of associated fields can only be done by using these recognized labels and by settingfields
to an empty string. Otherwise, iffields
are used, they must have a 1:! relationship with thefield_labels
, and the associated values will be displayed in the table without additional processing by the controller.<body> ... <div class="top"> ... </div> <div class="section"> <!-- Line items for non-recurring Lease invoices --> <apex:outputPanel rendered="{!AND(NOT(isSaleRecordType),NOT(isRecurringInvoice))}"> <h3>{!$Label.McLabs2__Lease_Commission_Items}</h3> <apex:outputPanel rendered="{!hasLeaseCommissionItems}"> <McLabs2:InvoiceLineItems invoice_id="{!invoice.Id}" field_labels="{!$Label.McLabs2__Line_Item}, {!$Label.McLabs2__Term}, {!commissionItemQuantityLabel}, {!commissionItemAmountLabel}, {!$Label.McLabs2__Area_Sq_Ft}, {!$Label.McLabs2__Amount}, {!$Label.McLabs2__Commission}, {!$Label.McLabs2__Commission_DollarSign}" fields="" total_label="{!$Label.McLabs2__Total_Lease_Commission_Items}"/> </apex:outputPanel> <apex:outputPanel rendered="{!hasNonLeaseCommissionItems}"> <McLabs2:InvoiceLineItems invoice_id="{!invoice.Id}" field_labels="{!$Label.McLabs2__Line_Item}, {!$Label.McLabs2__Type}, {!$Label.McLabs2__Area_Sq_Ft}, {!$Label.McLabs2__Commission}, {!$Label.McLabs2__Commission_DollarSign}" fields="" total_label="{!$Label.McLabs2__Total_Lease_Commission_Items}"/> </apex:outputPanel> </apex:outputPanel> <!-- Line items for non-recurring Sale invoices --> <apex:outputPanel rendered="{!AND(isSaleRecordType,NOT(isRecurringInvoice))}"> <h3>{!$Label.McLabs2__Sale_Commission_Items}</h3> <apex:outputPanel rendered="{!hasSaleCommissionItems}"> <McLabs2:InvoiceLineItems invoice_id="{!invoice.Id}" field_labels="{!$Label.McLabs2__Line_Item}, {!$Label.McLabs2__Type}, {!$Label.McLabs2__Amount}, {!$Label.McLabs2__Commission}, {!$Label.McLabs2__Commission_DollarSign}" fields="" total_label="{!$Label.McLabs2__Total_Sales_Commission}"/> </apex:outputPanel> </apex:outputPanel> </div> </body>
-
Step 6: Now we'll add a section for the billing schedule beneath the line items. The InvoiceBillingSchedule component can also calculate values based on a set of recognized labels, but in this case we'll specify the
fields
in order to control exactly what is displayed.<body> ... <div class="top"> ... </div> <div class="section"> ... </div> <div class="section"> <!-- Billing schedule for a non-recurring invoice --> <apex:outputPanel rendered="{!AND(NOT(isRecurringInvoice), hasInvoiceList)}"> <h3>{!$Label.McLabs2__Billing_Schedule}</h3> <McLabs2:InvoiceBillingSchedule invoice_id="{!invoice.Id}" field_labels="{!$Label.McLabs2__Invoice_Number}, {!$Label.McLabs2__Due_Date}, {!$Label.McLabs2__SubTotal}, {!$Label.McLabs2__Tax}, {!$Label.McLabs2__Total}, {!$Label.McLabs2__Amount_Paid}" fields="{!$ObjectType.McLabs2__Invoice__c.FieldSets.McLabs2__BillingScheduleFields}"/> </apex:outputPanel> <!-- Billing schedule for a recurring invoice --> <apex:outputPanel rendered="{!AND(NOT(isSaleRecordType),isRecurringInvoice)}"> <h3>{!$Label.McLabs2__Lease_Commission_Items}</h3> <apex:outputPanel rendered="{!hasRecurringInvoiceList}"> <McLabs2:InvoiceBillingSchedule invoice_id="{!invoice.Id}" field_labels="{!$Label.McLabs2__Invoice_Number}, {!$Label.McLabs2__Term}, {!$Label.McLabs2__Rent_Month}, {!$Label.McLabs2__Commission}, {!$Label.McLabs2__Commission_DollarSign}, {!$Label.McLabs2__Amount_Paid}" fields=""/> </apex:outputPanel> </apex:outputPanel> </div> </body>
-
Step 7: Finally, we'll add a section for Notes at the bottom of the invoice. There are few fields on the Invoice object that may have information that we want to include, so we'll utilize
<apex:outputPanel>
to add some line breaks between those fields only if those fields actually have values.<body> ... <div class="top"> ... </div> <div class="section"> ... </div> <div class="section"> ... </div> <div class="section"> <h3>{!$Label.McLabs2__Notes}</h3> <apex:outputText value="{!invoice.McLabs2__Notes__c}" escape="false" /> <apex:outputPanel rendered="{!If(invoice.McLabs2__Notes__c !='',true,false)}"> <br/><br/> </apex:outputPanel> <apex:outputText value="{!invoice.McLabs2__Invoice_Template__r.McLabs2__Notes__c}" escape="false" /> <apex:outputPanel rendered="{!If(invoice.McLabs2__Invoice_Template__r.McLabs2__Notes__c !='',true,false)}"> <br/><br/> </apex:outputPanel> <apex:outputText value="{!invoice.McLabs2__Payment_Information__r.McLabs2__Instructions__c}" escape="false" /> </div> </body>
-
Step 8: Success! You should now have a fully functional custom template component. The complete code for this example is below.
<apex:component controller="McLabs2.InvoiceComponentController" access="global"> <apex:styleSheet value="{!URLFOR($Resource.McLabs2__AptoResources, 'css/invoice.css')}"/> <apex:attribute name="invoice_id" type="Id" description="the Id of the invoice" assignTo="{!invoiceId}"/> <body> <McLabs2:InvoiceHeader invoice_id="{!invoice.Id}" fields="{!$ObjectType.McLabs2__Invoice__c.FieldSets.McLabs2__InvoiceHeaderFields}"/> <div class="top"> <div class="half address-section"> <div class="bill-to"> <!-- Bill To address to display if the Bill To field is empty or set to 'Recipient Account' --> <apex:outputPanel rendered="{!OR(invoice.Bill_To__c =='Recipient Account',invoice.Bill_To__c =='')}"> <McLabs2:InvoiceAddress invoice_id="{!invoice.Id}" fields="{!$ObjectType.McLabs2__Invoice__c.FieldSets.McLabs2__BillToRecipientAddressFields}"/> </apex:outputPanel> <!-- Bill To address to display for a Sale invoice with a Bill To of 'Property Ownership Entity' --> <apex:outputPanel rendered="{!AND(isSaleRecordType, invoice.Bill_To__c =='Property Ownership Entity')}"> <McLabs2:InvoiceAddress invoice_id="{!invoice.Id}" fields="Comp__r.Property__r.Ownership_Entity__c,Comp__r.Property__r.Ownership_Entity_Address__c"/> </apex:outputPanel> <!-- Bill To address to display for a Lease invoice with a Bill To of 'Property Ownership Entity' --> <apex:outputPanel rendered="{!AND(NOT(isSaleRecordType), invoice.Bill_To__c =='Property Ownership Entity')}"> <McLabs2:InvoiceAddress invoice_id="{!invoice.Id}" fields="Comp__r.Property_Leases__r.Ownership_Entity__c,Comp__r.Property_Leases__r.Ownership_Entity_Address__c"/> </apex:outputPanel> </div> <div class="leased-to"> <!-- Sold To address to display for a Sale invoice --> <apex:outputPanel rendered="{!isSaleRecordType}"> <McLabs2:InvoiceAddress title="{!$Label.McLabs2__Sold_to}" invoice_id="{!invoice.Id}" fields="Comp__r.Buyer_Company__r.Name,Comp__r.Property_Leases__r.Name,Comp__r.Property__r.Name,Comp__r.Property_Address__c,Comp__r.Property_City__c,Comp__r.Property_State__c,Comp__r.Property_Zip_Postal_Code__c,Comp__r.Property_Country__c"/> </apex:outputPanel> <!-- Sold To address to display for a Lease invoice --> <apex:outputPanel rendered="{!NOT(isSaleRecordType)}"> <McLabs2:InvoiceAddress title="{!$Label.McLabs2__Sold_to}" invoice_id="{!invoice.Id}" fields="Comp__r.Tenant__r.Name,Comp__r.Property_Leases__r.Name,Comp__r.Property__r.Name,Comp__r.Property_Address__c,Comp__r.Property_City__c,Comp__r.Property_State__c,Comp__r.Property_Zip_Postal_Code__c,Comp__r.Property_Country__c"/> </apex:outputPanel> </div> </div> <div class="half invoice-details-section"> <McLabs2:InvoiceDetails title="Invoice" invoice_id="{!invoice.Id}" field_labels="{!$Label.McLabs2__Invoice_Number}, {!$Label.McLabs2__InvoiceDate}, {!$Label.McLabs2__Terms}, {!$Label.McLabs2__Invoice_Due_Date}" fields="{!$ObjectType.McLabs2__Invoice__c.FieldSets.McLabs2__InvoiceDetailFields}"/> </div> </div> <div class="section"> <!-- Line items for non-recurring Lease invoices --> <apex:outputPanel rendered="{!AND(NOT(isSaleRecordType),NOT(isRecurringInvoice))}"> <h3>{!$Label.McLabs2__Lease_Commission_Items}</h3> <apex:outputPanel rendered="{!hasLeaseCommissionItems}"> <McLabs2:InvoiceLineItems invoice_id="{!invoice.Id}" field_labels="{!$Label.McLabs2__Line_Item}, {!$Label.McLabs2__Term}, {!commissionItemQuantityLabel}, {!commissionItemAmountLabel}, {!$Label.McLabs2__Area_Sq_Ft}, {!$Label.McLabs2__Amount}, {!$Label.McLabs2__Commission}, {!$Label.McLabs2__Commission_DollarSign}" fields="" total_label="{!$Label.McLabs2__Total_Lease_Commission_Items}"/> </apex:outputPanel> <apex:outputPanel rendered="{!hasNonLeaseCommissionItems}"> <McLabs2:InvoiceLineItems invoice_id="{!invoice.Id}" field_labels="{!$Label.McLabs2__Line_Item}, {!$Label.McLabs2__Type}, {!$Label.McLabs2__Area_Sq_Ft}, {!$Label.McLabs2__Commission}, {!$Label.McLabs2__Commission_DollarSign}" fields="" total_label="{!$Label.McLabs2__Total_Lease_Commission_Items}"/> </apex:outputPanel> </apex:outputPanel> <!-- Line items for non-recurring Sale invoices --> <apex:outputPanel rendered="{!AND(isSaleRecordType,NOT(isRecurringInvoice))}"> <h3>{!$Label.McLabs2__Sale_Commission_Items}</h3> <apex:outputPanel rendered="{!hasSaleCommissionItems}"> <McLabs2:InvoiceLineItems invoice_id="{!invoice.Id}" field_labels="{!$Label.McLabs2__Line_Item}, {!$Label.McLabs2__Type}, {!$Label.McLabs2__Amount}, {!$Label.McLabs2__Commission}, {!$Label.McLabs2__Commission_DollarSign}" fields="" total_label="{!$Label.McLabs2__Total_Sales_Commission}"/> </apex:outputPanel> </apex:outputPanel> </div> <div class="section"> <!-- Billing schedule for a non-recurring invoice --> <apex:outputPanel rendered="{!AND(NOT(isRecurringInvoice), hasInvoiceList)}"> <h3>{!$Label.McLabs2__Billing_Schedule}</h3> <McLabs2:InvoiceBillingSchedule invoice_id="{!invoice.Id}" field_labels="{!$Label.McLabs2__Invoice_Number}, {!$Label.McLabs2__Due_Date}, {!$Label.McLabs2__SubTotal}, {!$Label.McLabs2__Tax}, {!$Label.McLabs2__Total}, {!$Label.McLabs2__Amount_Paid}" fields="{!$ObjectType.McLabs2__Invoice__c.FieldSets.McLabs2__BillingScheduleFields}"/> </apex:outputPanel> <!-- Billing schedule for a recurring invoice --> <apex:outputPanel rendered="{!AND(NOT(isSaleRecordType),isRecurringInvoice)}"> <h3>{!$Label.McLabs2__Lease_Commission_Items}</h3> <apex:outputPanel rendered="{!hasRecurringInvoiceList}"> <McLabs2:InvoiceBillingSchedule invoice_id="{!invoice.Id}" field_labels="{!$Label.McLabs2__Invoice_Number}, {!$Label.McLabs2__Term}, {!$Label.McLabs2__Rent_Month}, {!$Label.McLabs2__Commission}, {!$Label.McLabs2__Commission_DollarSign}, {!$Label.McLabs2__Amount_Paid}" fields=""/> </apex:outputPanel> </apex:outputPanel> </div> <div class="section"> <h3>{!$Label.McLabs2__Notes}</h3> <apex:outputText value="{!invoice.McLabs2__Notes__c}" escape="false" /> <apex:outputPanel rendered="{!If(invoice.McLabs2__Notes__c !='',true,false)}"> <br/><br/> </apex:outputPanel> <apex:outputText value="{!invoice.McLabs2__Invoice_Template__r.McLabs2__Notes__c}" escape="false" /> <apex:outputPanel rendered="{!If(invoice.McLabs2__Invoice_Template__r.McLabs2__Notes__c !='',true,false)}"> <br/><br/> </apex:outputPanel> <apex:outputText value="{!invoice.McLabs2__Payment_Information__r.McLabs2__Instructions__c}" escape="false" /> </div> </body> </apex:component>
#####So Now What?
Now that you have a slick new invoice template, you probably want to do something useful with it, such as previewing it or sending it as an email attachment. So let's do that.
-
#####Previewing Your Invoice
Before we can preview our custom invoice component, we need to embed it in a Visualforce page. Navigate to Build > Develop > Pages and create a new Visualforce page using the code below. Refer to Creating Your First Page for additional details on working with Visualforce pages.
<apex:page standardController="McLabs2__Invoice__c" standardStylesheets="false" showHeader="false" applyBodyTag="false" renderAs="pdf" title="{!McLabs2__Invoice__c.Name}" > <body> <c:CustomInvoicePreview invoice_id="{!McLabs2__Invoice__c.Id}" /> </body> </apex:page>
Now that you have a Visualforce page containing your component, follow the steps below to create a button that will appear on invoice objects to allow you to preview your custom invoice.
- View the Invoice object, scroll down to Buttons, Links, and Actions, and click New Button or Link.
- Set an appropriate Label and Name for your button, and then set the Display Type to Detail Page Button, the Content Source to Visualforce Page, and Content to MyCustomInvoice. Click the Save button.
- View an Invoice and click the link for Edit Layout.
- Find your button under the Buttons section and drag it into the layout. Click the Save button.
-
#####Emailing Your Invoice
Sending your invoice as an attachment is a little more complicated, because we first need to create an email template for it. Refer to Creating Visualforce Email Templates for additional details on working with Visualforce email templates.
-
Navigate to the page under Administer > Communication Templates > Email Templates.
-
Select the folder for Apto Email Templates, and then click the New Template button.
-
Select Visualforce as the template type, and then click the Next button.
-
Check the box for Available For Use and fill in appropriate values for Email Template Name, Template Unique Name, Subject, and Recipeint Type. Click the Save button.
-
Now we will edit the template to so that it has dynamic values and includes our invoice as an attachment. Click the Edit Template button.
-
Under the tab for Email Content, paste the following code:
<messaging:emailTemplate subject="{!relatedTo.McLabs2__Invoice_Template__r.Company_Name__c} Invoice {!relatedTo.Name} for {!recipient.Account.Name}" recipientType="Contact" relatedToType="Invoice__c"> <messaging:plainTextEmailBody >{!if(recipient.Salutation = "", TRIM(LEFT(recipient.Salutation,2)),recipient.Salutation)} {!if(recipient.FirstName = "", TRIM(RIGHT(recipient.LastName ,27)) ,recipient.FirstName )}, Please see the attached invoice #{!relatedTo.Name} due on <apex:outputText value=" {0,date,medium}"><apex:param value="{!relatedTo.Due_Date__c}"/></apex:outputText>. After you review the attached invoice, please confirm that it was correct and that it will be submitted for payment. Thank you for your business. Sincerely, </messaging:plainTextEmailBody> <messaging:attachment renderAs="PDF" filename=" {!relatedTo.McLabs2__Invoice_Template__r.Company_Name__c} Invoice {!relatedTo.Name} for {!recipient.Account.Name}"> <c:MyCustomInvoice invoice_id="{!relatedTo.Id}"/> </messaging:attachment> </messaging:emailTemplate>
-
If you named your component differently, replace
CustomInvoicePreview
with the appropriate component name. Note that fields such as themessaging:emailTemplate
subject
andmessaging:attachment
filename
are dynamic based on invoice attributes. These can be changed to utilize any other invoice properties that are appropriate. -
Now we need to create a new page to allow use to access this template. Navigate to Build > Develop > Pages and create a new page with the following code, making sure to set the parameter for
sendEmailTemplate('MyCustomInvoiceTemplate')
to be the name of the email template you just created:<apex:page standardController="McLabs2__Invoice__c" extensions="McLabs2.InvoiceEmailTemplateController"> Loading email form... <apex:form > <apex:actionFunction name="sendEmailTemplate" action="{!sendEmailTemplate}" reRender="content"> <apex:param name="emailTemplateName" value="" /> </apex:actionFunction> </apex:form> <script> sendEmailTemplate('MyCustomInvoiceTemplate'); </script> </apex:page>
-
After saving your page, follow the steps for Previewing Your Invoice, making sure to set the Content value in Step 2 to the page that you just created.
-