How To Generate Invoices Using Python & Playwright

dboost.me
3 min readAug 5, 2023

--

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.

Generated Invoice

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>

--

--

No responses yet