Running a freelance or small business means constant repetitive work: invoices, payment follow-ups, expense tracking. I automated all of it with Python. Here's the exact code.
1. Generate Professional PDF Invoices Automatically
from reportlab.lib.pagesizes import letter
from reportlab.pdfgen import canvas
from datetime import datetime, timedelta
def generate_invoice(client_name, client_email, services, invoice_num=None):
if invoice_num is None:
invoice_num = f"INV-{datetime.now().strftime('%Y%m%d%H%M')}"
total = sum(item['amount'] for item in services)
due_date = datetime.now() + timedelta(days=30)
filename = f"invoice_{invoice_num}.pdf"
c = canvas.Canvas(filename, pagesize=letter)
width, height = letter
c.setFont("Helvetica-Bold", 24)
c.drawString(50, height - 60, "INVOICE")
c.setFont("Helvetica", 10)
c.drawString(50, height - 80, f"Invoice #: {invoice_num}")
c.drawString(50, height - 95, f"Date: {datetime.now().strftime('%B %d, %Y')}")
c.drawString(50, height - 110, f"Due: {due_date.strftime('%B %d, %Y')}")
c.setFont("Helvetica-Bold", 12)
c.drawString(50, height - 150, "Bill To:")
c.setFont("Helvetica", 11)
c.drawString(50, height - 168, client_name)
c.drawString(50, height - 183, client_email)
y = height - 230
c.setFont("Helvetica-Bold", 11)
c.drawString(50, y, "Description")
c.drawString(430, y, "Amount")
c.line(50, y - 5, 550, y - 5)
y -= 25
c.setFont("Helvetica", 11)
for item in services:
c.drawString(50, y, item['description'])
c.drawString(430, y, f"${item['amount']:,.2f}")
y -= 20
c.line(50, y - 5, 550, y - 5)
y -= 25
c.setFont("Helvetica-Bold", 12)
c.drawString(350, y, "TOTAL:")
c.drawString(430, y, f"${total:,.2f}")
c.save()
return filename
invoice = generate_invoice(
"Acme Corp",
"billing@acme.com",
[
{"description": "Web Development (40 hours @ $75/hr)", "amount": 3000},
{"description": "Domain & Hosting Setup", "amount": 150},
]
)
print(f"Invoice generated: {invoice}")
Run this once per client, adjust the numbers, and you have a professional PDF invoice in seconds.
2. Automated Payment Reminders
Chasing unpaid invoices is the most awkward part of freelancing. Automate it:
import smtplib, sqlite3
from email.mime.text import MIMEText
from datetime import datetime
def check_and_remind_overdue(db_path="invoices.db"):
conn = sqlite3.connect(db_path)
cursor = conn.cursor()
today = datetime.now().date()
cursor.execute("""
SELECT invoice_num, client_name, client_email, amount, due_date
FROM invoices WHERE paid = 0 AND due_date < ?
ORDER BY due_date ASC
""", (str(today),))
for inv_num, name, email, amount, due_date in cursor.fetchall():
days_overdue = (today - datetime.strptime(due_date, "%Y-%m-%d").date()).days
if days_overdue <= 7:
subject = f"Gentle Reminder: Invoice {inv_num}"
body = f"Hi {name}, just a reminder that Invoice {inv_num} for ${amount:,.2f} was due {due_date}. No worries, please process when you can!"
elif days_overdue <= 30:
subject = f"OVERDUE: Invoice {inv_num} — {days_overdue} days past due"
body = f"Hi {name}, Invoice {inv_num} for ${amount:,.2f} is now {days_overdue} days overdue. Please arrange payment ASAP."
else:
subject = f"Final Notice: Invoice {inv_num}"
body = f"Hi {name}, Invoice {inv_num} (${amount:,.2f}) is {days_overdue} days overdue. Immediate payment required."
send_email(email, subject, body) # your email sending function
print(f"Reminded {name}: {inv_num} ({days_overdue}d overdue)")
conn.close()
Set this to run every Monday morning via cron: 0 9 * * 1 python3 remind_overdue.py
3. Auto-Categorize Bank Statement Expenses
Export your bank transactions as CSV, then auto-categorize:
import pandas as pd
CATEGORIES = {
"Software": ["aws", "github", "notion", "slack", "figma", "adobe"],
"Marketing": ["facebook ads", "google ads", "mailchimp"],
"Travel": ["uber", "lyft", "airbnb", "delta"],
"Meals": ["restaurant", "doordash", "starbucks"],
"Office": ["amazon", "staples", "best buy"],
}
def categorize(description):
desc = description.lower()
for cat, keywords in CATEGORIES.items():
if any(kw in desc for kw in keywords):
return cat
return "Other"
def process_statement(csv_file):
df = pd.read_csv(csv_file, names=["date", "description", "amount"])
df["category"] = df["description"].apply(categorize)
summary = df.groupby("category")["amount"].agg(["sum", "count"])
for cat, row in summary.iterrows():
print(f"{cat:15} ${abs(row['sum']):8,.2f} ({int(row['count'])} txns)")
# Flag large unusual charges
threshold = df["amount"].abs().mean() * 3
unusual = df[df["amount"].abs() > threshold]
if len(unusual):
print("\n⚠️ Large transactions to review:")
print(unusual[["date", "description", "amount"]].to_string())
What This Saves Me
Running these scripts each week saves me about 3 hours — time I used to spend:
- Opening invoice templates and manually filling fields (20 min/invoice)
- Logging into bank portals and manually categorizing expenses (90 min/month)
- Awkwardly emailing clients about late payments (30 min each)
Over a year, that's 150+ hours back.
If you want all 12 of my business automation scripts (invoice gen, payment reminders, expense tracking, email automation, inventory monitoring, and more) in one package, I put them in the Python Business Automation Toolkit — $29, instant download, pay once.
Which of these would help your business most? Leave a comment below.
Top comments (0)