Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Default template

Subscription25 GoMeddo Subscription Management provides you want a standard layout which has limited customisation options. For now, you can:

  • Upload your logo in the administration

  • Upload your footer image in the administration

  • Change the terms and condition URL

Info

You need to upload a file to the attachments of the Administration, that is prefixed with Logo_ or Footer_ so GoMeddo Subscription Management will know which images to capture.

The layout of the default template is as follows:

...

To create a custom Invoice Layout you need to create a visualforce page that is rendered as a PDF. This page needs to have the Subscription25 GoMeddo Subscription Management Invoice object as it standardController. You can extend this with a custom class to get additional functionalities in the pdf.

Code Block
<apex:page standardController="Subscription25__Invoice__c" renderAs="pdf" sidebar="false" showHeader="false" applyBodyTag="false">
  Invoice Date: {!Subscription25__Invoice__c.Subscription25__Invoice_Date__c}  
  etc..
</apex:page>

...

To use this template you must create an “Invoice template record” in Subscription25 GoMeddo Subscription Management and assign it to the administrations in which you want to use this template. This allows you to create different layouts for different administrations.

...

On your administration, you should have a related list called Administration Invoice Layouts in which you should create a record to link this template to that administration.

...

You can also use the following template

Expand
titleTemplate details

You need to do three steps:

  1. Upload static resource

  2. Create Apex Class + Test class

  3. Create Visualforce page

Upload the following static resource with the name ‘invoice’

View file
nameinvoice.zip

Class Ctrl_InvoiceLayout:

Code Block
public with sharing class Ctrl_InvoiceLayout {

    private Subscription25__Invoice__c invoice;
    public InvoiceItem[] items {get; private set;}

    public Id companyLogoId {get; private set;}

    public Decimal sum {get; private set;}
    public Decimal total {get; private set;}
    public Decimal taxAmount {get; private set;}

    public Boolean hasICP {get; private set;}

    public Ctrl_InvoiceLayout(ApexPages.StandardController stdController) {
        this.invoice = (Subscription25__Invoice__c)stdController.getRecord();

        List<Order> orders = this.getOrders();
        this.items = new InvoiceItem[]{};
        this.hasICP = false;
        for (Order order : orders) {
            for (OrderItem item : order.OrderItems) {
                this.items.add(new InvoiceItem(item));
                this.hasICP = this.hasICP || item.Subscription25__Use_ICP__c;
            }
        }

        this.taxAmount = 0;
        this.sum = 0;
        for (InvoiceItem item : this.items) {
            this.sum += item.price * item.quantity;
            this.taxAmount += item.price * item.quantity * item.tax / 100;
        }
        this.sum = Ctrl_InvoiceLayout.format(this.sum);
        this.taxAmount = Ctrl_InvoiceLayout.format(this.taxAmount);
        this.total = Ctrl_InvoiceLayout.format(this.sum + this.taxAmount);

        //try to load an attachment of the administration that starts with logo, if present, use that as a logo, otherwise, fall back
        //to the company logo as defined in the custom setting
        List<Attachment> atts = this.getAttachments();
        this.companyLogoId = atts.size() > 0 ? atts.get(0).Id : Ctrl_InvoiceLayout.getLogoId();
    }

    private List<Attachment> getAttachments() {
        Id adminId = this.invoice.Subscription25__Administration__c;
        String query = 'SELECT Name FROM Attachment WHERE ParentId = :adminId AND Name LIKE \'Logo%\'';
        return Database.query(query);
    }

    private List<Order> getOrders() {
        Id invoiceId = this.invoice.Id;
        return [
            SELECT Name, AccountId, Account.Name, 
                Subscription25__Reseller_Customer__c, Subscription25__Reseller_Customer__r.Name, 
                Subscription25__Invoice__r.Subscription25__Account__r.Name,
                (
                    SELECT Id, Subscription25__VAT_Percentage__c, Quantity,
                        UnitPrice, Description, Subscription25__Use_ICP__c,
                        Subscription25__Hide_VAT_On_Invoice__c, Subscription25__Product__c, Subscription25__Product__r.Name,
                        OrderId, Order.Name, Order.Id, Order.AccountId, Order.Account.Name,
                        Order.Subscription25__Reseller_Customer__c, Order.Subscription25__Reseller_Customer__r.Name,
                        Order.Subscription25__Invoice__c, Order.Subscription25__Invoice__r.Subscription25__Account__c, Order.Subscription25__Invoice__r.Subscription25__Account__r.Name
                    FROM OrderItems
                )
                FROM Order
                WHERE Subscription25__Invoice__c = :invoiceId
        ];
    }

    private static Decimal format(Decimal amount) {
        return amount.setScale(2, RoundingMode.HALF_UP);
    }

    private static Id getLogoId(){
        String query = 'SELECT Id FROM Document WHERE DeveloperName LIKE \'%Subscription25_Logo\' LIMIT 1';
        List<Document> docs = Database.query(query);
        return docs.size() > 0 ? docs[0].Id : null;
    }


    private class InvoiceItem {
        public Decimal quantity {get; private set;}
        public String accountLabel {get; private set;} //used for resellers and bill to parent
        public String label {get; private set;}
        public Decimal price {get; private set;}
        public Decimal tax {get; private set;}
        public String description {get; private set;}

        public InvoiceItem(OrderItem oi) {
            this.quantity = oi.Quantity;
            this.label = oi.Subscription25__Product__r.Name;
            this.price = oi.UnitPrice;
            this.tax = oi.Subscription25__VAT_Percentage__c;
            this.description = oi.Description;

            if(oi.Order.Subscription25__Reseller_Customer__c != null){
                this.accountLabel = oi.Order.Subscription25__Reseller_Customer__r.Name;
            }
            if(oi.Order.AccountId != oi.Order.Subscription25__Invoice__r.Subscription25__Account__c){
                this.accountLabel = oi.Order.Account.Name;
            }
        }

        public Decimal getTotal() {
            Decimal total = this.price * this.quantity;
            return Ctrl_InvoiceLayout.format(total);
        }
    }
}

Test Class Test_Ctrl_InvoiceLayout:

Code Block
@isTest
private class Test_Ctrl_InvoiceLayout {
    @testSetup static void setup(){
        String entity = 'GoMeddo Subscription Management';

        Subscription25__Administration__c admin = new Subscription25__Administration__c(
                Subscription25__Billing_Email_Address__c = 's25@test.com',
                Subscription25__Billing_Name__c = entity,
                Subscription25__Billing_Street__c = 'teststraat',
                Subscription25__Billing_Postal_Code__c = '1234ab',
                Subscription25__Billing_City__c = 'Amsterdam',
                Subscription25__Billing_Country__c = 'Netherlands',
                Subscription25__Bank__c = 'Test Bank',
                Subscription25__BIC__c = 'test bic',
                Subscription25__IBAN__c = 'test iban',
                Subscription25__VAT_Number__c = '21',
                Subscription25__Chamber_Of_Commerce_Number__c = 'test cocn',
                Subscription25__Website__c = 'www.test.com'
        );
        admin.Name = entity;
        admin.Subscription25__Invoice_Number_Prefix__c = '123';
        Database.insert(admin);

        Account acc = new Account();
        acc.Name = 'Test Account';
        acc.BillingStreet = 'Hoogte Kadijk 38-1';
        acc.BillingPostalCode = '1018BM';
        acc.BillingCity = 'Amsterdam';
        acc.BillingCountry = 'Netherlands';
        acc.ShippingStreet = 'Hoogte Kadijk 38-1';
        acc.ShippingPostalCode = '1018BM';
        acc.ShippingCity = 'Amsterdam';
        acc.ShippingCountry = 'Netherlands';
        Database.insert(acc);

        Contact con = new Contact();
        con.LastName = 'van Test';
        con.FirstName = 'Testje';
        con.Email = 'test@test.com';
        Database.insert(con);

        Product2 prod = new Product2(); 
        prod.Name = 'Test Product';
        Database.insert(prod);

        PricebookEntry pbe = new PricebookEntry();
        pbe.Pricebook2Id = Test.getStandardPricebookId();
        pbe.Product2Id = prod.Id;
        pbe.UnitPrice = 1;
        Database.insert(pbe);

        Subscription25__Invoice__c i = new Subscription25__Invoice__c();
        i.Subscription25__Account__c = acc.Id;
        i.Subscription25__Administration__c = admin.Id;
        i.Subscription25__Invoice_Date__c = Date.today();
        i.Subscription25__Description__c = 'invoice description';
        i.Subscription25__Purchase_Order_Number__c = '1234';
        i.Subscription25__Billing_Country__c = 'NLD';
        Database.insert(i);


        Subscription25__VATRate__c vatRate = new Subscription25__VATRate__c();
        vatRate.Subscription25__Active__c = true;
        vatRate.Subscription25__Default__c = true;
        vatRate.Subscription25__VAT_Percentage__c = 19;
        Database.insert(vatRate);

        Subscription25__VAT_Code__c vatCode = new Subscription25__VAT_Code__c();
        vatCode.Subscription25__Administration__c = admin.Id;
        vatCode.Subscription25__VATRate__c = vatRate.Id;
        vatCode.Subscription25__VAT_Code__c = 'VH';
        Database.insert(vatCode);

        Subscription25__Administration_Product__c ap = new Subscription25__Administration_Product__c();
        ap.Subscription25__Administration__c = admin.id;
        ap.Subscription25__Product__c = prod.Id;
        ap.Subscription25__VATRate__c = vatRate.Id;
        ap.Subscription25__Ledger_Account__c = '1000';
        Database.insert(ap);

        Subscription25__Debtor_Number__c dbNmbr = new Subscription25__Debtor_Number__c();
        dbNmbr.Subscription25__Administration__c = admin.Id;
        dbNmbr.Subscription25__Debtor_Number__c = '123';
        dbNmbr.Subscription25__Account__c = acc.Id;
        Database.insert(dbNmbr);        

        Order ord = new Order();
        ord.AccountId = acc.Id;
        ord.Subscription25__Administration__c = admin.Id;
        ord.Pricebook2Id = Test.getStandardPricebookId();
        ord.status = 'draft';
        ord.Subscription25__Invoice__c = i.Id;
        ord.EffectiveDate = Date.today();
        Database.insert(ord);

        OrderItem ordItem = new OrderItem();
        ordItem.UnitPrice = 12;
        ordItem.Quantity = 1;
        ordItem.OrderId = ord.Id;
        ordItem.Product2Id = prod.Id;
        ordItem.Subscription25__Product__c = prod.Id;
        ordItem.Subscription25__VAT_Code__c = 'VH';
        ordItem.Subscription25__VAT_Percentage__c = 12;
        Database.insert(ordItem);

        
    }

    @IsTest
    static void test_constructor(){

        PageReference pageRef = Page.Invoice; //name of visualforce page
        Test.setCurrentPage(pageRef);
        ApexPages.StandardController sc = new ApexPages.StandardController([
            SELECT Id, Subscription25__Account__c,Subscription25__Administration__c,Subscription25__Invoice_Date__c,
                Subscription25__Description__c,Subscription25__Purchase_Order_Number__c,Subscription25__Billing_Country__c 
            FROM Subscription25__Invoice__c 
            LIMIT 1
        ]);

        Ctrl_InvoiceLayout ctrl = new Ctrl_InvoiceLayout(sc);
    }

}

Visualforce Invoice:

Code Block
<apex:page standardController="Subscription25__Invoice__c" extensions="Ctrl_InvoiceLayout" renderAs="pdf" sidebar="false" showHeader="false" applyBodyTag="false">
    <head>
    <link media="print" rel="stylesheet" type="text/css" href="{!URLFOR($Resource.invoice, 'css/invoice.css')}" />
    </head>
    <body>
        <div class="header">
            <apex:image value="/servlet/servlet.FileDownload?file={!companyLogoId}" styleClass="logo"/>
        </div>
        <div class="header">
            <div class="logoContainer">
                <apex:image value="/servlet/servlet.FileDownload?file={!companyLogoId}" styleClass="logo" />
            </div>
            <table class="invoiceRulesHeader">
                <thead>
                    <tr>
                        <th class="number">Quantity</th>
                        <th class="description">Description</th>
                        <th class="price right">Price</th>
                        <th class="total right">Total</th>
                        <th class="vat right">VAT</th>
                    </tr>
                </thead>
            </table>
        </div>
        <div class="footer">
            <div class="container">
                <ul>
                    <li>
                        <apex:outputField value="{!Subscription25__Invoice__c.Subscription25__Administration__r.Subscription25__Billing_Name__c}" />
                    </li>
                    <li>
                        <apex:outputText value="{!Subscription25__Invoice__c.Subscription25__Administration__r.Subscription25__Billing_Street__c}" />
                    </li>
                    <li>
                        <apex:outputText value="{!Subscription25__Invoice__c.Subscription25__Administration__r.Subscription25__Billing_Postal_Code__c} {!Subscription25__Invoice__c.Subscription25__Administration__r.Subscription25__Billing_City__c}" />
                    </li>
                    <li class="last">
                        <apex:outputText value="{!Subscription25__Invoice__c.Subscription25__Administration__r.Subscription25__Billing_Country__c}" />
                    </li>
                </ul>
                <ul>
                    <li>
                        <apex:outputText value="VAT {!Subscription25__Invoice__c.Subscription25__Administration__r.Subscription25__VAT_Number__c}" />
                    </li>
                    <li>
                        <apex:outputText value="Bank {!Subscription25__Invoice__c.Subscription25__Administration__r.Subscription25__Bank__c}" />
                    </li>
                    <li>
                        <apex:outputText value="IBAN {!Subscription25__Invoice__c.Subscription25__Administration__r.Subscription25__IBAN__c}" />
                    </li>
                    <li class="last">
                        <apex:outputText value="BIC_Swift {!Subscription25__Invoice__c.Subscription25__Administration__r.Subscription25__BIC__c}" />
                    </li>
                </ul>
            </div>
            <div class="pageNumbers">Page <span class="pagenumber"/> Of <span class="pagecount"/></div>
        </div>
        <div class="container">
            <div class="invoicevat">
                <apex:outputText value="INVOICE" />
            </div>
            <table class="contactDetails">
                <tr>
                    <td class="contactName"><apex:outputText value="{!Subscription25__Invoice__c.Subscription25__Account__r.Name}" /></td>
                </tr>
                <apex:outputPanel layout="none" rendered="true">
                    <tr>
                        <td>
                            <apex:outputText value="Attn {!Subscription25__Invoice__c.Subscription25__Contact_Person__r.Name}" rendered="{!Subscription25__Invoice__c.Subscription25__Contact_Person__c != null}"/>
                        </td>
                    </tr>
                </apex:outputPanel>
                <tr>
                    <td><apex:outputText value="{!Subscription25__Invoice__c.Subscription25__Billing_Street__c}" /></td>
                </tr>
                <tr>
                    <td><apex:outputText value="{!Subscription25__Invoice__c.Subscription25__Billing_Postal_Code__c} {!Subscription25__Invoice__c.Subscription25__Billing_City__c}" /></td>
                </tr>
                <tr>
                    <td><apex:outputText value="{!Subscription25__Invoice__c.Subscription25__Billing_Country__c}" /></td>
                </tr>
                <tr>
                    <td><apex:outputText value="{!Subscription25__Invoice__c.Subscription25__VAT_Number__c}" /></td>
                </tr>
            </table>

            <table class="invoiceDetails">
                <tr>
                    <td class="leftColumn">InvoiceDate:</td>
                    <td><apex:outputField value="{!Subscription25__Invoice__c.Subscription25__Invoice_Date__c}" /></td>
                </tr>
                <tr>
                    <td>DueDate:</td>
                    <td><apex:outputField value="{!Subscription25__Invoice__c.Subscription25__Due_Date__c}" /></td>
                </tr>
                <tr>
                    <td>InvoiceNumber:</td>
                    <td><apex:outputField value="{!Subscription25__Invoice__c.Subscription25__Invoice_Number__c}" /></td>
                </tr>
            </table>
            <apex:outputPanel rendered="{!NOT(ISBLANK(Subscription25__Invoice__c.Subscription25__Description__c))}" layout="none">
                <div class="subject">
                    <apex:outputText value="Regarding: {!Subscription25__Invoice__c.Subscription25__Description__c}" />
                </div>
            </apex:outputPanel>
            <table class="invoiceRules">
                <thead>
                    <tr>
                        <th class="number">Quantity</th>
                        <th class="description">Description</th>
                        <th class="price right">Price</th>
                        <th class="total right">Total</th>
                        <th class="vat right">VAT</th>
                    </tr>
                </thead>
                <tbody>
                    <apex:repeat var="item" value="{!items}">
                        <tr>
                            <td class="number right">
                                <apex:outputText value="{0, number,###,###,##0.00}">
                                    <apex:param value="{!item.quantity}" />
                                </apex:outputText>
                            </td>
                            <td class="description">
                                <div><apex:outputText value="{!item.accountLabel}" /></div>
                                <apex:outputText value="{!item.label}" />
                                <div class="subText"><apex:outputText value="{!item.description}" /></div>
                            </td>
                            <td class="price right">
                                <apex:outputText value="€{0, number,###,###,##0.00}">
                                    <apex:param value="{!item.price}" />
                                </apex:outputText>
                            </td>
                            <td class="total right">
                                <apex:outputText value="€{0, number,###,###,##0.00}">
                                    <apex:param value="{!item.total}" />
                                </apex:outputText>
                            </td>
                            <td class="vat right">
                                <apex:outputText value="{0, number,###,###,##0.00}%">
                                    <apex:param value="{!item.tax}" />
                                </apex:outputText>
                            </td>
                        </tr>
                    </apex:repeat>
                </tbody>
            </table>
            <div class="paymentDetails">
                <table class="invoiceRulesFooter">
                    <tbody>
                        <tr>
                            <td class="number"></td>
                            <td colspan="2" class="totalText">Subtotal (ExclTax)</td>
                            <td class="total right">
                                <apex:outputText value="€{0, number,###,###,##0.00}">
                                    <apex:param value="{!sum}" />
                                </apex:outputText>
                            </td>
                            <td class="vat"></td>
                        </tr>
                        <tr>
                            <td class="number"></td>
                            <td colspan="2" class="totalText">Tax</td>
                            <td class="total right">
                                <apex:outputText value="€{0, number,###,###,##0.00}">
                                    <apex:param value="{!taxAmount}" />
                                </apex:outputText>
                            </td>
                            <td class="vat"></td>
                        </tr>
                    </tbody>
                    <tfoot>
                        <tr>
                            <td class="number"></td>
                            <td colspan="2" class="totalText">Total (InclTax)</td>
                            <td class="total right">
                                <apex:outputText value="€{0, number,###,###,##0.00}">
                                    <apex:param value="{!total}" />
                                </apex:outputText>
                            </td>
                            <td class="vat"></td>
                        </tr>
                    </tfoot>
                </table>
                <apex:outputPanel styleClass="timeleyPayment" rendered="{!hasICP}">
                    ICPLawMessage
                </apex:outputPanel>
                <div class="timeleyPayment">
                    Please reference invoice number <strong>{!Subscription25__Invoice__c.Subscription25__Invoice_Number__c}</strong> with your payment. We appreciate your timely payment.
                </div>
                <table class="paymentInfo">
                    <tr>
                        <td class="first">Wire transfer to:</td>
                        <td></td>
                    </tr>
                    <tr>
                        <td>Bank Name:</td>
                        <td><apex:outputField value="{!Subscription25__Invoice__c.Subscription25__Administration__r.Subscription25__Bank__c}" /></td>
                    </tr>
                    <tr>
                        <td>Bank Account Name:</td>
                        <td><apex:outputField value="{!Subscription25__Invoice__c.Subscription25__Administration__r.Subscription25__Billing_Name__c}" /></td>
                    </tr>
                    <tr>
                        <td>BIC Swift:</td>
                        <td><apex:outputField value="{!Subscription25__Invoice__c.Subscription25__Administration__r.Subscription25__BIC__c}" /></td>
                    </tr>
                    <tr>
                        <td>IBAN:</td>
                        <td><apex:outputField value="{!Subscription25__Invoice__c.Subscription25__Administration__r.Subscription25__IBAN__c}" /></td>
                    </tr>
                </table>
                <apex:outputPanel rendered="{!NOT(ISBLANK(Subscription25__Invoice__c.Subscription25__Foot_Note__c))}" layout="none">
                    <div class="via-email">
                        <apex:outputField value="{!Subscription25__Invoice__c.Subscription25__Foot_Note__c}" />
                    </div>
                </apex:outputPanel>
            </div>
        </div>
    </body>
    <apex:outputText rendered="none" value="Subscription25__Invoice__c.Subscription25__Administration__c}" />
</apex:page>

Next up

Now you can setup Set up Automatic Invoice Creation and E-billing