Files
verify/backend/app/services/invoice_pdf.py
Mohamed Mathar Irfan ed6610d5d8 Initial project upload
2026-07-28 17:57:02 +05:30

103 lines
3.5 KiB
Python

"""Server-side GST invoice PDF (reportlab). INR, 18% GST."""
import io
from datetime import datetime
from reportlab.lib import colors
from reportlab.lib.pagesizes import A4
from reportlab.lib.units import mm
from reportlab.pdfgen import canvas
def _inr(paise_or_rupees: int) -> str:
return f"Rs. {paise_or_rupees:,.2f}"
def build_invoice_pdf(invoice: dict, org: dict) -> bytes:
buf = io.BytesIO()
c = canvas.Canvas(buf, pagesize=A4)
w, h = A4
y = h - 25 * mm
# Header
c.setFillColor(colors.HexColor("#0b1f4d"))
c.setFont("Helvetica-Bold", 20)
c.drawString(20 * mm, y, "VerifyPack")
c.setFont("Helvetica", 9)
c.setFillColor(colors.grey)
c.drawString(20 * mm, y - 6 * mm, "Tax Invoice")
c.setFont("Helvetica-Bold", 12)
c.setFillColor(colors.black)
c.drawRightString(w - 20 * mm, y, f"Invoice {invoice.get('invoice_number', '')}")
c.setFont("Helvetica", 9)
c.setFillColor(colors.grey)
date = invoice.get("created_at")
date_str = date.strftime("%d %b %Y") if isinstance(date, datetime) else str(date or "")
c.drawRightString(w - 20 * mm, y - 6 * mm, f"Date: {date_str}")
y -= 22 * mm
c.setStrokeColor(colors.HexColor("#e2e8f0"))
c.line(20 * mm, y, w - 20 * mm, y)
# Billed to
y -= 10 * mm
c.setFillColor(colors.black)
c.setFont("Helvetica-Bold", 10)
c.drawString(20 * mm, y, "Billed To")
c.setFont("Helvetica", 9)
c.drawString(20 * mm, y - 5 * mm, org.get("name", ""))
if org.get("gstin"):
c.drawString(20 * mm, y - 10 * mm, f"GSTIN: {org['gstin']}")
if org.get("address"):
c.drawString(20 * mm, y - 15 * mm, org["address"][:60])
c.setFont("Helvetica-Bold", 10)
c.drawRightString(w - 20 * mm, y, "From")
c.setFont("Helvetica", 9)
c.drawRightString(w - 20 * mm, y - 5 * mm, "VerifyPack Technologies")
c.drawRightString(w - 20 * mm, y - 10 * mm, "Bengaluru, Karnataka, India")
# Line items table
y -= 30 * mm
c.setFillColor(colors.HexColor("#f1f5f9"))
c.rect(20 * mm, y, w - 40 * mm, 8 * mm, fill=1, stroke=0)
c.setFillColor(colors.HexColor("#334155"))
c.setFont("Helvetica-Bold", 9)
c.drawString(23 * mm, y + 2.5 * mm, "Description")
c.drawRightString(w - 23 * mm, y + 2.5 * mm, "Amount")
subtotal = invoice.get("subtotal", 0)
gst = invoice.get("gst_amount", 0)
total = invoice.get("total", 0)
y -= 10 * mm
c.setFillColor(colors.black)
c.setFont("Helvetica", 9)
period = invoice.get("period", "")
c.drawString(23 * mm, y, f"{invoice.get('plan_name', 'Plan')} subscription {period}".strip())
c.drawRightString(w - 23 * mm, y, _inr(subtotal))
# Totals
y -= 14 * mm
c.line(w - 80 * mm, y + 6 * mm, w - 20 * mm, y + 6 * mm)
c.setFont("Helvetica", 9)
c.drawRightString(w - 55 * mm, y, "Subtotal")
c.drawRightString(w - 23 * mm, y, _inr(subtotal))
y -= 6 * mm
c.drawRightString(w - 55 * mm, y, "GST (18%)")
c.drawRightString(w - 23 * mm, y, _inr(gst))
y -= 8 * mm
c.setFont("Helvetica-Bold", 11)
c.drawRightString(w - 55 * mm, y, "Total")
c.drawRightString(w - 23 * mm, y, _inr(total))
# Footer
c.setFont("Helvetica", 8)
c.setFillColor(colors.grey)
c.drawString(20 * mm, 20 * mm, f"Place of supply: {invoice.get('place_of_supply', 'Karnataka')}")
c.drawString(20 * mm, 15 * mm, "This is a computer-generated invoice and does not require a signature.")
c.drawRightString(w - 20 * mm, 15 * mm, f"Status: {str(invoice.get('status', '')).upper()}")
c.showPage()
c.save()
return buf.getvalue()