Generating Invoices

Written on 3 June 2020. Tagged with freelancing, tooling.

I started working as a freelance programmer, and at some point I needed to issue an invoice for my first customer to pay for my services. I wanted to send an electronic invoice, in other words: first generate a PDF file, then send the file by email.

Let me explain how I automated the PDF file generation.

It may come as a surprise, but I decided not to use LaTeX because I anticipated it would be a lot of work to apply custom styles for the invoice to look like my freelancing website [fr]. I will reconsider if some day my website uses LaTeX.css.

Instead, I went with HTML, CSS and JavaScript.

Starting with HTML and CSS

The idea was to render each invoice as a web page looking like my website, then print the web page to PDF, a feature available in most web browsers nowadays.

I created a file named template.html and I designed the invoice to be printed on a single A4 sheet. Even on one sheet, I managed to include the mandatory information [fr] supposed to appear on a French invoice.

Thanks to Flexbox, including a header and a footer was easy:

<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <style>
        body {
            display: flex;
            flex-direction: column;
            font-size: 10pt;
            height: 297mm;
            margin: 0 auto;
            width: 210mm;
        }
        header {
            font-size: larger;
        }
        main {
            flex: 1;
        }
        footer {
            font-size: smaller;
        }
    </style>
</head>
<body>
    <header>
        <!-- invoice number -->
    </header>
    <main>
        <!-- customer address, invoice lines and total price -->
    </main>
    <footer>
        <!-- company address and number -->
    </footer>
</body>

At this point, I could copy the template.html file and tailor the new invoice to my customer by updating the details by hand.

Adding JavaScript to the mix

I improved the previous version by using JavaScript as a templating engine. The idea was to streamline the invoice editing process after copying the template.html file.

I included Vue.js using a <script> tag and wrote a small Vue application:

<main id="app">
    <table>
        <tr>
            <th>Description</th>
            <th>Quantity</th>
            <th>Unit price</th>
            <th>Subtotal</th>
        </tr>
        <tr v-for="line in lines">
            <td>{{ line.description }}</td>
            <td>{{ line.quantity | format_quantity }}</td>
            <td>{{ line.unit_price | format_price }}</td>
            <td>{{ line.quantity * line.unit_price | format_price }}</td>
        </tr>
    </table>
    <ul>
        <li v-if="with_discount">
            Subtotal:
            {{ subtotal | format_price }}
        </li>
        <li v-if="with_discount">
            {{ discount_rate | format_rate }} discount:
            {{ discount | format_price }}
        </li>
        <li>
            Total:
            {{ total | format_price }}
        </li>
    </ul>
</main>
var app = new Vue({
    el: '#app',
    data: {
        lines: [{
            description: "Day of feature development",
            quantity: 10,
            unit_price: 1000,
        },{
            description: "Day of server administration",
            quantity: 3,
            unit_price: 1000,
        }],
        discount_rate: 0.1,
    },
    computed: {
        discount: function () {
            return this.discount_rate * this.subtotal;
        },
        subtotal: function () {
            var cb = (acc, line) => acc + line.quantity * line.unit_price;
            return this.lines.reduce(cb, 0);
        },
        total: function () {
            return this.subtotal - this.discount;
        },
        with_discount: function () {
            return this.discount_rate > 0;
        },
    },
    filters: {
        format_price: function (p) {
            var options = { style: 'currency', currency: 'EUR' };
            return new Intl.NumberFormat('fr', options).format(p);
        },
        format_quantity: function (q) {
            var options = { style: 'decimal', minimumFractionDigits: 1 };
            return new Intl.NumberFormat('fr', options).format(q);
        },
        format_rate: function (r) {
            var options = { style: 'percent' };
            return new Intl.NumberFormat('fr', options).format(r);
        },
    }
});

With this new version, I could declare the invoice lines and an optional discount rate, then let the invoice render itself. As one can see, I made sure computing total price and formatting numbers is done automatically to avoid errors and to be consistent.

Consecutive invoice numbers

As a last step, I needed to make all invoice numbers consecutive ones to comply with French regulation. So I automated copying the template.html file with a Makefile rule:

YEAR := $(shell date +%Y)
COUNT := $(shell find . -name "$(YEAR)-*.html" |wc -l)
NEXT := $(shell printf "$(YEAR)-%02d.html" "$(shell expr $(COUNT) + 1)")

.PHONY: new
new:  ## Create new invoice from template
    @echo Counted $(COUNT) invoices created in $(YEAR)
    @cp template.html $(NEXT)
    @echo Created $(NEXT)

Ultimately, to issue an invoice for a customer, I follow these steps:

  1. run make new in a terminal,
  2. declare invoice lines in JavaScript,
  3. load and print the web page to PDF.

This is not rocket science, but it may help others.