DEV Community

Brad
Brad

Posted on

Python Invoice Generator: Automated PDF Billing With Payment Reminders

Python Invoice Generator: Automated PDF Billing With Payment Reminders

Late invoices cause late payments. I automated my entire billing system with 150 lines of Python.

PDF Invoice Generator

from reportlab.lib import colors
from reportlab.lib.pagesizes import letter
from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph
from reportlab.lib.styles import getSampleStyleSheet
from datetime import datetime, timedelta

def create_invoice(client_name, client_email, services, invoice_num):
    filename = f"invoice_{invoice_num}.pdf"
    doc = SimpleDocTemplate(filename, pagesize=letter)
    styles = getSampleStyleSheet()
    elements = []

    elements.append(Paragraph(f"INVOICE #{invoice_num}", styles['Title']))
    elements.append(Paragraph(f"Date: {datetime.now().strftime('%B %d, %Y')}", styles['Normal']))
    elements.append(Paragraph(f"Due: {(datetime.now() + timedelta(days=30)).strftime('%B %d, %Y')}", styles['Normal']))
    elements.append(Paragraph(f"Bill To: {client_name} ({client_email})", styles['Normal']))

    rows = [['Description', 'Qty', 'Rate', 'Amount']]
    total = 0
    for s in services:
        amount = s['qty'] * s['rate']
        total += amount
        rows.append([s['desc'], str(s['qty']), f"${s['rate']:.2f}", f"${amount:.2f}"])
    rows.append(['', '', 'TOTAL', f"${total:.2f}"])

    table = Table(rows, colWidths=[250, 50, 100, 100])
    table.setStyle(TableStyle([
        ('BACKGROUND', (0, 0), (-1, 0), colors.darkblue),
        ('TEXTCOLOR', (0, 0), (-1, 0), colors.white),
        ('GRID', (0, 0), (-1, -1), 1, colors.black),
        ('FONTNAME', (0, -1), (-1, -1), 'Helvetica-Bold'),
    ]))
    elements.append(table)
    doc.build(elements)
    return filename, total
Enter fullscreen mode Exit fullscreen mode

Send Invoice by Email

import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email.mime.text import MIMEText
from email import encoders

def email_invoice(invoice_file, client_email, client_name, total, due):
    msg = MIMEMultipart()
    msg['From'] = 'you@gmail.com'
    msg['To'] = client_email
    msg['Subject'] = f"Invoice - ${total:.2f} due {due}"
    msg.attach(MIMEText(f"Hi {client_name}, please find your invoice attached.", 'plain'))

    with open(invoice_file, 'rb') as f:
        part = MIMEBase('application', 'octet-stream')
        part.set_payload(f.read())
        encoders.encode_base64(part)
        part.add_header('Content-Disposition', f'attachment; filename={invoice_file}')
        msg.attach(part)

    with smtplib.SMTP('smtp.gmail.com', 587) as smtp:
        smtp.starttls()
        smtp.login('you@gmail.com', 'app-password')
        smtp.send_message(msg)
Enter fullscreen mode Exit fullscreen mode

Automated Payment Reminders

from datetime import datetime, timedelta
import sqlite3

def run_reminders():
    conn = sqlite3.connect('invoices.db')
    c = conn.cursor()

    # 3 days before due
    soon = (datetime.now() + timedelta(days=3)).strftime('%Y-%m-%d')
    c.execute('SELECT * FROM invoices WHERE due_date = ? AND paid = 0 AND r1_sent = 0', (soon,))
    for inv in c.fetchall():
        send_reminder(inv, 'upcoming')
        c.execute('UPDATE invoices SET r1_sent = 1 WHERE id = ?', (inv[0],))

    # 7 days overdue
    overdue = (datetime.now() - timedelta(days=7)).strftime('%Y-%m-%d')
    c.execute('SELECT * FROM invoices WHERE due_date < ? AND paid = 0 AND r2_sent = 0', (overdue,))
    for inv in c.fetchall():
        send_reminder(inv, 'overdue')
        c.execute('UPDATE invoices SET r2_sent = 1 WHERE id = ?', (inv[0],))
    conn.commit()
Enter fullscreen mode Exit fullscreen mode

Schedule

0 8 * * * /usr/bin/python3 invoice_reminders.py
Enter fullscreen mode Exit fullscreen mode

Result: Went from 4 hours/week on billing to 20 minutes.

The complete version with Stripe/PayPal integration, expense tracking, and profit/loss reports is in the toolkit below.


Get 50+ Python automation scripts for $9: Python Business Automation Toolkit

Top comments (0)