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:
- run make new in a terminal,
- declare invoice lines in JavaScript,
- load and print the web page to PDF.
This is not rocket science, but it may help others.