Earning money is fun, generating invoices is not. Let’s automate the latter. We are going to generate html
invoices first and then convert them to pdf
using playwright
capabilities. Here you fill find the code which will save your time if you decide to create your own invoices.
Generating HTML
We are going to create a simple jinja
template and render it using python to an html document. The html template is going to be attached at the bottom because of it’s verbosity. Here is the python
code which will render the template given the context data.
# file: render_html.py
from jinja2 import FileSystemLoader, Environment
invoice_number = 1
context = {
"invoice_number": invoice_number,
"invoice_header": "Invoice for Delivering Pizza",
"amount": 10,
"currency": "USD",
"account": "account_id",
"account_iban": "account_iban",
"swift": "account_swift",
"work_description": "Delivering pizza on motorbike",
"person": "James Bond",
"email": "james@bond.mi6",
"phone": "+4476898123428",
"billed_to": "MI-6",
"billed_to_address": "85 Albert Embankment",
"payment_received_date": "2023-08-05"
}
template_loader = FileSystemLoader("./")
template_env = Environment(loader=template_loader)
template = template_env.get_template("invoice_sample.html")
invoice_html = template.render(context)
file_name = f"{invoice_number}.html"
with open(file_name, "w") as html_file:
html_file.write(invoice_html)
Command python render_html.py
is going to render the given template and generate an html file.
Generating PDF
We are going to use node
and playwright
to generate a pdf from a given html. Here is the code which will do that:
// file: generate_pdf.js
const fs = require('fs');
const path = require('path');
const {chromium} = require('playwright');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto("file:///" + path.resolve("1.html"));
await page.pdf({path: path.resolve("1.pdf")});
await browser.close();
})()
Command node generate_pdf.js
is going to load 1.html
file in the playwright
browser and generate a pdf
from it.
Conclusions
If you can automate. It’s fun and you will learn new things. You can easily scale the given example, by keeping you invoices data in the spreadsheet, downloading it as csv
then generating an html
per line and tranforming it into pdf
and sending them to your TAX service.
HTML template is here (it’s too long):
<!DOCTYPE html>
<html>
<head>
<title>Invoice</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
}
.container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
border: 1px solid #ccc;
}
table {
width: 100%;
border-collapse: collapse;
}
h2 {
margin-bottom: 50px;
text-align: center;
}
th, td {
padding: 10px;
text-align: left;
border-bottom: 1px solid #ccc;
}
.invoice-details {
margin-bottom: 20px;
text-align: left;
}
.invoice-details p {
margin: 0;
font-size: 16px;
}
.invoice-details strong {
font-weight: bold;
}
.invoice-items {
margin-top: 60px;
margin-bottom: 20px;
}
.invoice-items th {
font-weight: bold;
text-align: left;
}
.invoice-items td {
text-align: left;
}
.invoice-total {
text-align: right;
margin-right: 30px;
}
.invoice-total strong {
font-size: 20px;
font-weight: bold;
}
.bottom {
margin-top: 40px;
text-align: right;
}
.info {
margin-top: 60px;
}
.signature {
width: 200px;
height: auto;
}
@media print {
.container {
border: none;
}
}
</style>
</head>
<body>
<div class="container">
<h2>{{ invoice_header }}</h2>
<div class="invoice-details">
<p><strong>Invoice number:</strong> {{ invoice_number }}</p>
<p><strong>Date:</strong> {{ payment_received_date }}</p>
<p><strong>Billed to:</strong> {{ billed_to }}</p>
<p><strong>Address: </strong> {{ billed_to_address }}</p>
</div>
<div class="invoice-items">
<table>
<thead>
<tr>
<th>Description</th>
<th>Amount</th>
<th>Currency</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{ work_description }}</td>
<td>{{ amount }}</td>
<td>{{ currency }}</td>
</tr>
</tbody>
</table>
</div>
<p class="info">
Paid in {{ currency }} to {{ person }},
IBAN <strong>{{account_iban}}</strong>,
SWIFT <strong>{{ swift }}</strong>
</p>
<div class="invoice-details bottom">
<p><strong>{{ person }}</strong></p>
<p><strong>Email:</strong> {{ email }}</p></p>
<p><strong>Phone:</strong> {{ phone }}</p>
</div>
</div>
</body>
</html>