Invoice PDF Generator
PythonAutomationBuilds a clean PDF invoice, HST included, from a simple JSON file. Sample data only.
Run it
cd samples/invoice-generator
python make_invoice.py # needs reportlabFiles: INV-0001.pdf · invoice.json · make_invoice.py

Input
{"number":"INV-0001","date":"2026-10-18","due":"2026-11-17",
"from":{"name":"Kirk Davies Studio","address":"Ottawa, ON"},
"to":{"name":"Example Client Inc. (sample data)","address":"123 Sample St, Ottawa, ON"},
"items":[["Python data-cleanup script",1,250.00],["Web scraper setup & CSV export",1,400.00],["Revisions (hourly)",2,60.00]],
"tax_rate":0.13,"tax_label":"HST (13%)"}
Code
"""Generate a clean PDF invoice from a JSON file (sample data only)."""
import json, sys
from reportlab.lib.pagesizes import letter
from reportlab.lib import colors
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle
from reportlab.lib.styles import getSampleStyleSheet
def build(src="invoice.json"):
d = json.load(open(src)); st = getSampleStyleSheet()
out = f"{d['number']}.pdf"
rows = [["Description", "Qty", "Unit", "Amount"]]
sub = 0
for desc, q, u in d["items"]:
sub += q * u; rows.append([desc, q, f"${u:,.2f}", f"${q*u:,.2f}"])
tax = round(sub * d["tax_rate"], 2)
rows += [["", "", "Subtotal", f"${sub:,.2f}"], ["", "", d["tax_label"], f"${tax:,.2f}"], ["", "", "Total (CAD)", f"${sub+tax:,.2f}"]]
t = Table(rows, colWidths=[270, 50, 90, 90])
t.setStyle(TableStyle([("BACKGROUND", (0,0), (-1,0), colors.HexColor("#2f6f5e")), ("TEXTCOLOR", (0,0), (-1,0), colors.white),
("ALIGN", (1,0), (-1,-1), "RIGHT"), ("LINEBELOW", (0,0), (-1,-4), 0.5, colors.grey), ("FONTNAME", (2,-1), (-1,-1), "Helvetica-Bold")]))
story = [Paragraph(f"<b>{d['from']['name']}</b><br/>{d['from']['address']}", st["Normal"]), Spacer(1, 18),
Paragraph(f"Invoice {d['number']}", st["Title"]),
Paragraph(f"Date: {d['date']} Due: {d['due']}<br/><br/><b>Bill to:</b> {d['to']['name']}<br/>{d['to']['address']}", st["Normal"]),
Spacer(1, 18), t, Spacer(1, 24), Paragraph("Sample invoice generated from JSON. Not a real transaction.", st["Italic"])]
SimpleDocTemplate(out, pagesize=letter).build(story)
print(f"wrote {out}: subtotal ${sub:.2f}, tax ${tax:.2f}, total ${sub+tax:.2f}")
if __name__ == "__main__":
build(*sys.argv[1:])