Quaderno Templates: A Walkthrough

In this tutorial we'll walk you, step-by-step, through the creation of a brand new document template. We'll be using almost all the features in the Quaderno templating engine, giving you a flavour for how everything fits together, and a guide you can reference, follow and tweak for your own usage.

Skeleton

First, it's important to remember that a template is just an HTML document with some placeholders for information that Quaderno supplies to it during document generation (and some sugar on top to make it smart about what to show and not show). For anyone who has used something like Mailchimp templates, you'll understand how  variables can be added.

As a template is a HTML document at its heart, it's a good idea to start with your vision for a finished document, after Quaderno has inserted everything.

Here is our end result. We'll work backwards from this:

<html>
<head>
  <title>My invoice #00025</title>
</head>
<body>
  <div id="content">
    <table>      
      <tr>
        <td colspan="2"><img src="http://example.org/background.png" width="150" /></td>
      </tr>      
      <tr>
        <td colspan="2" class="right">
          <p>
            <strong>Sheldon Cooper</strong><br />
            2311 N. Los Robles. Apartment 4A<br />
            Pasadena. Los Angeles county, California.<br />
            United States of America, Planet Earth.
          </p>
        </td>
      </tr>
      <tr>
        <td colspan="2" class="center">
          <h1>Invoice</h1>
        </td>
      </tr>
      <tr>
        <td>
          <p><strong>Number:</strong> #00025</p>
          <p><strong>Issue date:</strong> 07/12/2012</p>
          <p><strong>PO Number:</strong> 0001</p>
        </td>
        <td>&nbsp;</td>
      </tr>
      <tr>
        <td colspan="2">
          <table id="items">
            <thead>
              <tr>
                <th>Description</th>
                <th>Quantity</th>
                <th class="right">Unit price</th>
                <th class="right">Amount</th>
              </tr>
            </thead>
            <tfoot>
              <tr>
                <td colspan="4">
                  <p>Subtotal: 1345.00 USD</p>
                  <p>Discount: 10%</p>
                  <p>Tax 1: 18%</p>
                  <p>Total: 1428.39 USD</p>
                  <p>Exchange: 1098.26 EUR</p>
                </td>
              </tr>
            </tfoot>
            <tbody>
              <tr>
                <td>Item 1</td>
                <td>2</td>
                <td class="right">25.00 USD</td>
                <td class="right">50.00 USD</td>
              </tr>
              <tr>
                <td>Item 2</td>
                <td>1</td>
                <td class="right">250.00 USD</td>
                <td class="right">250.00 USD</td>
              </tr>
              <tr>
                <td>Item 3</td>
                <td>1</td>
                <td class="right">1045.00 USD</td>
                <td class="right">1045.00 USD</td>
              </tr>
            </tbody>
          </table>
        </td>
      </tr>
      <tr>
        <td colspan="2">
            <p><strong>Due date:</strong> 30/12/2013</p>
            <p><strong>Payment details:</strong> Cash</p>
            <p><strong>Notes:</strong> Bazinga!</p>
        </td>
      </tr>
    </table>
  </div>
  <div id="footer">
    <p>
      <strong>Geek Physicians, Inc. - Tax ID: 481516</strong><br />
      42 The Answer St., New York, NY 4815 - USA.<br />
      Phone: 4-815-162342 - E-mail: thecakeisalie@geekphys.org
    </p>
  </div>
</body>
</html><br>

With our document looking good and containing all the information we need (it's a good idea to check with your accountant at this point, or consult a reference by your governing revenue body), we can get started breaking this sucka down into a form that can work for every eventuality.

Header

Let's take it from the top. The literal top: the header.

You happen to have a beautiful logo, but what if you use this template for another account as your business empire expands? And what if the logo URL changes?

It is always better to refer to the account.logo_url variable, defined in your Quaderno account settings so that the template will work no matter where its used. So let's change it:

{% if account.logo_url != "" %}
  <tr>
    <td colspan="2"><img src="{{account.logo_url}}" width="150" /></td>
  </tr>
{% endif %}<br>

Why is our tag surrounded by that {% if ... %} block? Well, it is possible that a logo has not been set in the account, so it is always a good practice to check first if variables exist. This goes for every variable you are unsure will exist.

As a general rule, you want to do this for all of your optional information, as well as for the optional information for your customers.

Now it's time for the contact information. Not every customer can be as cool as Sheldon Cooper. Let's make this thing a little more personal:

<p>
  <strong>{{contact.full_name}}</strong><br />
  <optional>{{contact.tax_id}}<br /></optional>
  {{contact.street_line_1}}<br />
  {{contact.city}} {{contact.region}} {{contact.postal_code}}<br />
  {{contact.country}}
</p><br>

Easy, isn't it? Remember, you can always head to the syntax reference to look for more possible tags.

Core

Let's focus on the main body of our invoice: the invoice itself.

We'll start with the meat of it - the items we're charging for and their information, line-by-line:

<td>
  <p><strong>{{labels.number}}: </strong>{{document.number}}</p>
  <p><strong>{{labels.issue_date}}: </strong>{{document.issue_date | date: "%d/%m/%Y"}}</p>
  {% if document.po_number != "" %}<p><strong>{{labels.po_number}}: </strong>{{document.po_number}}</p>{% endif %}
</td><br>

There are a couple of interesting things here:

  • The issue_date is a date and, as such, its output can be formatted using the date filter (more info in the Liquid Syntax page).
  • As we did with the account logo (account.logo_url) in the header, here document.po_number is an optional field, so we first check if it exists.
  • Note the use of labels in this snippet of code. These labels are used to allow for translation into other languages, so you should use them as much as possible. You can see all the labels here.

Now, it is time to list the actual items.

The code in our skeleton is completely hardcoded at present, and we need to genericise it so it will work no matter how many items we have or what they happen to be.

Luckily, we have the tools for that! Here we go:

<table id="items">
  <thead>
    <tr>
      <th>{{labels.description}}</th>
      <th>{{labels.quantity}}</th>
      <th class="right">{{labels.unit_price}}</th>
      <th class="right">{{labels.amount}}</th>
    </tr>
  </thead>
  <tfoot>
    <tr>
      <td colspan="4">
        <p>{{labels.subtotal}}: {{document.subtotal}}</p>
        {% if document.discount != 0 %}
        <p>{{labels.discount}}: {{document.discount}}</p>
        {% endif %}
        {% for tax in document.taxes %}
        <p>{{tax.label}}: {{tax.amount}}</p>
        {% endfor %}
        <p>{{labels.total}}: {{document.total}}</p>
        {% if document.exchange != document.total %}<p>{{labels.exchange}}: {{document.exchange}}</p>{% endif %}
      </td>
    </tr>
  </tfoot>
  <tbody>
    {% for item in document.items %}
    <tr>
      <td>{{item.description}}</td>
      <td>{{item.quantity}}</td>
      <td class="right">{{item.unit_price}}</td>
      <td class="right">{{item.subtotal}}</td>
    </tr>
    {% endfor %} 
  </tbody>
</table><br>

Again, labels are used to ensure correct display in different languages. But the most important point here is how we traverse the list of items in the document.

This is done with a for loop. We use document.items, which is a collection of objects, and we traverse through each object in the collection. This way, it is possible to print as many rows as there are items in the collection. No more, no less.

If you need more information on how to use the for blocks, please refer to the syntax page.

Finally, every invoice needs the remaining important stuff, such as total, discounts or taxes applied:

<td colspan="2">
    {% if document.due_date > 0 %}
    <p><strong>{{labels.due_date}}: </strong>{{document.due_date}}</p>
    {% endif %}
    {% if document.payment_details != "" %}
    <p><strong>{{labels.payment_details}}: </strong>{{document.payment_details | textilize}}</p>
    {% endif %}
    {% if document.notes != "" %}
    <p><strong>{{labels.notes}}: </strong>{{document.notes | textilize}}</p>
    {% endif %}
</td><br>

This snippet should not present any difficulty at all now that you have been inducted into the Quaderno Templates matrix, you little Neo you.

There is only one new thing, which is the textilize filter. This filter takes the input (document.payment_details or document.notes) and outputs its content with HTML (if any) processed.

Footer

Whew, we're on the home stretch now folks, bear with us!

No document is complete without a nice footer to round things off and give the eyes a balm. Let's set ours up for generic success (we might not make great parents...):

<div id="footer">
  <p>
    <strong>
      {{account.full_name}} - {{labels.tax_id}}: {{account.tax_id}}
    </strong> <br />
    {{account.street_line_1}} - {{account.city}} {{account.region}} {{account.postal_code}} - {{account.country}}.
    <br />{{labels.phone}}: {{account.phone_1}} - {{labels.email}}: {{account.email}}
  </p>
</div><br>

As before, we've used labels where we could, and swapped hardcoded values for our crop of handy Quaderno-Templates variables.

Final enhancements

Even though our template is now a fully working masterpiece of template artistry, we could and should still enhance a couple of things:

The word Invoice in the middle of the document may be labeled

<td colspan="2" class="center">
  <h1>{{labels.invoice}}</h1>
</td><br>

The title of the HTML page can also be labeled:

<title>{{labels.invoice}}: {{document.number}}</title><br>

And because we all need not only data but also graphics, why don't you take some time to apply your own custom CSS? ;)

Summing up

We hope this tutorial made things clearer for you, and we can't wait to see the eye-soothingly beautiful templates you manage to create for yourselves.

As an FYI, you can download the full example here (with CSS included). It might help you get off the ground with your own template, as well as giving you a look at how the CSS and the template work together.

Remember, you can get more information on the  syntax page and on the variables and labels references.

Go forth and create fabulous template art, people! Let's make financial documents the best-looking things in your customers' inboxes, together.

Still need help? Contact Us Contact Us