🔧 ProgrammierungRequest lifecycle: HandlerMapping HandlerAdapter resolvers(16.09.2026 um 00:08 Uhr)
🔧 ProgrammierungChapter 1 - The Funkiest of All Machines(16.09.2026 um 00:13 Uhr)
🔧 AI Nachrichten President Trump Called Nvidia’s Jensen Huang About A.I. Slowdown(15.09.2026 um 23:45 Uhr)
🔧 AI Nachrichten Google’s Simulated Fruit Fly Brain Did Not Write This Article(16.09.2026 um 00:00 Uhr)
🔧 ProgrammierungRequest lifecycle: HandlerMapping HandlerAdapter resolvers(16.09.2026 um 00:08 Uhr)
🔧 ProgrammierungChapter 1 - The Funkiest of All Machines(16.09.2026 um 00:13 Uhr)
🔧 AI Nachrichten President Trump Called Nvidia’s Jensen Huang About A.I. Slowdown(15.09.2026 um 23:45 Uhr)
🔧 AI Nachrichten Google’s Simulated Fruit Fly Brain Did Not Write This Article(16.09.2026 um 00:00 Uhr)

🔧 Programmierung 🕛 vor 2 Monaten 5 Min Lesezeit
0

Build a GST Invoice Generator in 87 Lines of Python

↗ Quelle (dev.to)
🗣️ Stimme:
📑 Inhaltsübersicht

If you freelance or run a small business in India, you have probably paid for invoicing software that does one thing: multiply numbers and put them in a PDF. Today we will build that ourselves — a GST-compliant tax invoice generator that reads line items from a CSV and produces a clean PDF, in 87 lines of Python.



It handles the part everyone gets wrong: the CGST/SGST vs IGST split. Intra-state sales split the tax into equal CGST and SGST halves; inter-state sales charge a single IGST. Our script decides automatically by comparing the first two digits of the buyer's and seller's GSTINs (those digits are the state code).






What you'll need



One dependency:




CODE
pip install fpdf2






fpdf2 is a maintained, pure-Python PDF library — no system packages, works the same on Windows, Mac, and Linux.






The input format



Keep your line items in a plain CSV. Anyone on your team can edit this in Excel:




CODE
description,hsn,qty,rate,gst_pct
Website development,998314,1,45000,18
Annual hosting,998315,1,12000,18
SSL certificate,998316,2,1500,18









The full code






CODE
"""GST invoice generator — reads line items from CSV, outputs a PDF invoice."""
import csv
import sys
from datetime import date
from fpdf import FPDF

SELLER = {
"name": "Acme Tech Services Pvt Ltd",
"address": "221 MG Road, Bengaluru, KA 560001",
"gstin": "29ABCDE1234F1Z5",
"state_code": "29",
}


def load_items(path):
with open(path, newline="") as f:
return [
{
"desc": r["description"],
"hsn": r["hsn"],
"qty": float(r["qty"]),
"rate": float(r["rate"]),
"gst_pct": float(r["gst_pct"]),
}
for r in csv.DictReader(f)
]


def compute_totals(items, interstate):
rows, subtotal, tax_total = [], 0.0, 0.0
for it in items:
taxable = it["qty"] * it["rate"]
tax = taxable * it["gst_pct"] / 100
subtotal += taxable
tax_total += tax
rows.append((it, taxable, tax))
return rows, subtotal, tax_total


def build_pdf(rows, subtotal, tax_total, buyer, interstate, out_path):
pdf = FPDF()
pdf.add_page()
pdf.set_font("Helvetica", "B", 16)
pdf.cell(0, 10, "TAX INVOICE", align="C", new_x="LMARGIN", new_y="NEXT")
pdf.set_font("Helvetica", size=10)
pdf.cell(0, 6, f"{SELLER['name']} | GSTIN: {SELLER['gstin']}",
new_x="LMARGIN", new_y="NEXT")
pdf.cell(0, 6, SELLER["address"], new_x="LMARGIN", new_y="NEXT")
pdf.cell(0, 6, f"Invoice date: {date.today():%d-%m-%Y} | Bill to: "
f"{buyer['name']} (GSTIN: {buyer['gstin']})",
new_x="LMARGIN", new_y="NEXT")
pdf.ln(4)
pdf.set_font("Helvetica", "B", 9)
widths = (70, 20, 15, 25, 15, 25)
for w, h in zip(widths, ("Description", "HSN", "Qty", "Rate (Rs)",
"GST %", "Amount (Rs)")):
pdf.cell(w, 7, h, border=1)
pdf.ln()
pdf.set_font("Helvetica", size=9)
for it, taxable, _ in rows:
cells = (it["desc"], it["hsn"], f"{it['qty']:g}",
f"{it['rate']:,.2f}", f"{it['gst_pct']:g}", f"{taxable:,.2f}")
for w, v in zip(widths, cells):
pdf.cell(w, 7, str(v), border=1)
pdf.ln()
pdf.ln(3)
pdf.set_font("Helvetica", "B", 10)
pdf.cell(0, 6, f"Taxable value: Rs {subtotal:,.2f}",
new_x="LMARGIN", new_y="NEXT")
if interstate:
pdf.cell(0, 6, f"IGST: Rs {tax_total:,.2f}",
new_x="LMARGIN", new_y="NEXT")
else:
pdf.cell(0, 6, f"CGST: Rs {tax_total / 2:,.2f} SGST: "
f"Rs {tax_total / 2:,.2f}", new_x="LMARGIN", new_y="NEXT")
pdf.cell(0, 8, f"Grand total: Rs {subtotal + tax_total:,.2f}",
new_x="LMARGIN", new_y="NEXT")
pdf.output(out_path)


if __name__ == "__main__":
csv_path = sys.argv[1] if len(sys.argv) > 1 else "items.csv"
buyer = {"name": "Globex Retail LLP", "gstin": "27PQRSX5678G1Z3"}
interstate = buyer["gstin"][:2] != SELLER["state_code"]
rows, subtotal, tax_total = compute_totals(load_items(csv_path), interstate)
build_pdf(rows, subtotal, tax_total, buyer, interstate, "invoice.pdf")
print(f"invoice.pdf written - grand total Rs {subtotal + tax_total:,.2f}")






Run it:




CODE
python gst_invoice.py items.csv
# invoice.pdf written - grand total Rs 70,800.00









How it works



Loading (load_items). csv.DictReader turns each CSV row into a dict keyed by the header row, so the code reads like the spreadsheet. We cast qty, rate, and gst_pct to floats up front so the math functions never deal with strings.



The tax math (compute_totals). Each line's taxable value is qty x rate, and tax is a simple percentage of that. We accumulate a subtotal and a tax total and keep the per-row figures for the PDF table. The interstate decision happens once, in the entrypoint: buyer GSTIN state code != seller state code means IGST applies. In our example the buyer's GSTIN starts with 27 (Maharashtra) and the seller's with 29 (Karnataka), so the invoice shows a single IGST line. Change the buyer's GSTIN to start with 29 and you will see it split into equal CGST and SGST halves instead — no other change needed.



The PDF (build_pdf). fpdf2's cell() API is old-school but predictable: each cell has a fixed width, border=1 draws the table lines, and new_y="NEXT" moves the cursor down a row. The widths tuple is the entire layout — adjust six numbers and the whole table reflows. Total layout code: about 40 lines, and you own every pixel of it.






Extending it



A few upgrades that each take only a handful of lines: pull SELLER and the buyer from a JSON config instead of hardcoding; add an invoice number that auto-increments from a counter file; loop over a folder of CSVs to batch-generate a month of invoices; or add pdf.image("logo.png", x=10, y=8, w=30) for letterhead branding.



One honest caveat: this covers the common single-rate service invoice. If you deal with cess, reverse charge, or e-invoicing (IRN/QR codes), those are regimes of their own — treat this as the foundation, not a compliance department.



That's a working invoice pipeline in 87 lines — CSV in, compliant PDF out, and the tax split handled correctly without a subscription.



Follow me on Twitter @automate_archit for daily AI automation tips.

Vollständiger Original-Artikel
Den kompletten Beitrag mit allen Details direkt auf dev.to lesen.
↗ Original-Artikel auf dev.to lesen
Wie bewertest du diesen Beitrag?
1 Klick Feedback
Teilen mit Netzwerk & Team:

Community-Analysen & Experten-Meinungen 0

Verfasse deine eigene Analyse, teile Workarounds oder diskutiere diesen Vorfall im Blog.
Noch keine Community-Analyse verfasst. Markiere einen Textabschnitt oder klicke oben auf Eigene Analyse verfassen“!
Community Pulse: Relevanz-Einschätzung
1 Klick Experten-Votum
🔴 Akute Relevanz 0%
🟡 In Evaluierung 0%
🟢 Keine Auswirkung 0%
Spannende Innovation 0%
Verwandte Story-Cluster & Quellen (Vektor-KI)
Port 8095 Engine
1 Quelle
Protect Kubernetes Services with OAuth2 Proxy, Gateway API, Traefik, and Pocket ID
1 Quelle
Request lifecycle: HandlerMapping HandlerAdapter resolvers
1 Quelle
The best n8n fix I found this month was boring: lower your agent concurrency settings before touching the prompt
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Build a GST Invoice Generator in 87 Lines of Python

Thematisch verwandte Begriffe: Build, Invoice, Generator, Lines · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...