← All samples

Invoice PDF Generator

PythonAutomation

Builds 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 reportlab

Files: INV-0001.pdf · invoice.json · make_invoice.py

Invoice preview

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']} &nbsp; 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:])