Skip to content

Instantly share code, notes, and snippets.

@james-gibson
Last active September 16, 2015 22:26
Show Gist options
  • Save james-gibson/5517aa72bf96ef929408 to your computer and use it in GitHub Desktop.
Save james-gibson/5517aa72bf96ef929408 to your computer and use it in GitHub Desktop.
Creating Custom Invoice Templates in Apto

Apto Logo

##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 class data-line as well as data-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 class address-line as well as address-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 class details-line as well as details-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 and fields 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 and fields 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 and fields 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 and fieldSetMembers, 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 the Invoice_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 the Commission_Amount__c for each commission line item. This class can accept both fieldSetLabels and fieldSetMembers, however, if no fieldSetMembers 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 and Amount_Paid__c. The value of total is the sum of each of those calculations. This class can accept both fieldSetLabels and fieldSetMembers, however, if no fieldSetMembers 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 set renderA="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 the fields using a field set or a comma-delimited string. We will use a field set called InvoiceHeaderFields 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 as Label.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 setting fields to an empty string. Otherwise, if fields are used, they must have a 1:! relationship with the field_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.

    1. View the Invoice object, scroll down to Buttons, Links, and Actions, and click New Button or Link.
    2. 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.
    3. View an Invoice and click the link for Edit Layout.
    4. 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.

    1. Navigate to the page under Administer > Communication Templates > Email Templates.

    2. Select the folder for Apto Email Templates, and then click the New Template button.

    3. Select Visualforce as the template type, and then click the Next button.

    4. 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.

    5. 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.

    6. 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>
      
    7. If you named your component differently, replace CustomInvoicePreview with the appropriate component name. Note that fields such as the messaging:emailTemplate subject and messaging:attachment filename are dynamic based on invoice attributes. These can be changed to utilize any other invoice properties that are appropriate.

    8. 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>
      
    9. 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment