Build a Freelance Automation System in Python
Tags: python, automation, freelance, productivity
Why Freelancers Need Automation
Every freelancer wastes 10+ hours per week on repetitive tasks: sending invoices, following up on proposals, tracking time, managing clients. A Python automation system can reclaim those hours.
In this tutorial, you'll build a complete freelance automation system that handles proposals, invoicing, and client management.
The Architecture
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Email Parser │────▶│ Proposal Engine │────▶│ Invoice System │
│ (Gmail API) │ │ (Auto-generate) │ │ (Stripe/Crypto)│
└─────────────────┘ └──────────────────┘ └─────────────────┘
│ │ │
▼ ▼ ▼
┌─────────────────────────────────────────────────────────────────┐
│ Client Database (SQLite) │
│ Projects • Invoices • Proposals • Time Logs │
└─────────────────────────────────────────────────────────────────┘
Core Implementation
import sqlite3
from datetime import datetime, timedelta
from pathlib import Path
import json
import smtplib
from email.mime.text import MIMEText
class FreelanceAutomation:
"""Complete freelance business automation."""
def __init__(self, db_path="freelance.db"):
self.db = sqlite3.connect(db_path)
self._init_database()
def _init_database(self):
"""Create all tables if they don't exist."""
self.db.executescript("""
CREATE TABLE IF NOT EXISTS clients (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
email TEXT,
company TEXT,
hourly_rate REAL DEFAULT 100.0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS projects (
id INTEGER PRIMARY KEY,
client_id INTEGER REFERENCES clients(id),
name TEXT NOT NULL,
status TEXT DEFAULT 'active',
budget REAL,
start_date DATE,
deadline DATE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS time_logs (
id INTEGER PRIMARY KEY,
project_id INTEGER REFERENCES projects(id),
hours REAL NOT NULL,
description TEXT,
date DATE DEFAULT CURRENT_DATE,
billable BOOLEAN DEFAULT 1
);
CREATE TABLE IF NOT EXISTS invoices (
id INTEGER PRIMARY KEY,
project_id INTEGER REFERENCES projects(id),
amount REAL NOT NULL,
status TEXT DEFAULT 'draft',
sent_date DATE,
due_date DATE,
paid_date DATE,
payment_method TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS proposals (
id INTEGER PRIMARY KEY,
client_id INTEGER REFERENCES clients(id),
project_name TEXT NOT NULL,
amount REAL,
status TEXT DEFAULT 'draft',
sent_date DATE,
response_date DATE,
response TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
""")
self.db.commit()
def add_client(self, name, email=None, company=None, hourly_rate=100.0):
"""Add a new client."""
cursor = self.db.execute(
"INSERT INTO clients (name, email, company, hourly_rate) VALUES (?, ?, ?, ?)",
(name, email, company, hourly_rate)
)
self.db.commit()
return cursor.lastrowid
def start_project(self, client_id, name, budget=None, deadline=None):
"""Start a new project for a client."""
cursor = self.db.execute(
"INSERT INTO projects (client_id, name, budget, start_date, deadline) VALUES (?, ?, ?, ?, ?)",
(client_id, name, budget, datetime.now().date(), deadline)
)
self.db.commit()
return cursor.lastrowid
def log_time(self, project_id, hours, description=""):
"""Log time against a project."""
cursor = self.db.execute(
"INSERT INTO time_logs (project_id, hours, description) VALUES (?, ?, ?)",
(project_id, hours, description)
)
self.db.commit()
return cursor.lastrowid
def get_project_hours(self, project_id):
"""Get total billable hours for a project."""
result = self.db.execute(
"SELECT COALESCE(SUM(hours), 0) FROM time_logs WHERE project_id = ? AND billable = 1",
(project_id,)
).fetchone()
return result[0]
def generate_invoice(self, project_id):
"""Generate an invoice for unbilled hours."""
# Get project and client info
project = self.db.execute(
"""SELECT p.*, c.name as client_name, c.email as client_email, c.hourly_rate
FROM projects p JOIN clients c ON p.client_id = c.id WHERE p.id = ?""",
(project_id,)
).fetchone()
if not project:
return None
# Get unbilled hours
hours = self.db.execute(
"""SELECT SUM(hours) FROM time_logs
WHERE project_id = ? AND billable = 1 AND id NOT IN
(SELECT time_log_id FROM invoice_items WHERE invoice_id IN
(SELECT id FROM invoices WHERE project_id = ?))""",
(project_id, project_id)
).fetchone()[0] or 0
if hours == 0:
return None
amount = hours * project[8] # hourly_rate
# Create invoice
due_date = datetime.now().date() + timedelta(days=30)
cursor = self.db.execute(
"INSERT INTO invoices (project_id, amount, due_date) VALUES (?, ?, ?)",
(project_id, amount, due_date)
)
self.db.commit()
return {
"invoice_id": cursor.lastrowid,
"client": project[7],
"project": project[2],
"hours": hours,
"rate": project[8],
"amount": amount,
"due_date": due_date.isoformat()
}
def get_overdue_invoices(self):
"""Get all overdue invoices."""
return self.db.execute(
"""SELECT i.*, p.name as project_name, c.name as client_name, c.email
FROM invoices i
JOIN projects p ON i.project_id = p.id
JOIN clients c ON p.client_id = c.id
WHERE i.status = 'sent' AND i.due_date < ?""",
(datetime.now().date(),)
).fetchall()
def get_revenue_report(self, start_date=None, end_date=None):
"""Generate a revenue report."""
if not start_date:
start_date = datetime.now().date() - timedelta(days=30)
if not end_date:
end_date = datetime.now().date()
result = self.db.execute(
"""SELECT
COUNT(*) as invoice_count,
COALESCE(SUM(amount), 0) as total_revenue,
COALESCE(SUM(CASE WHEN paid_date IS NOT NULL THEN amount ELSE 0 END), 0) as collected,
COALESCE(SUM(CASE WHEN paid_date IS NULL THEN amount ELSE 0 END), 0) as outstanding
FROM invoices
WHERE created_at BETWEEN ? AND ?""",
(start_date.isoformat(), end_date.isoformat())
).fetchone()
return {
"period": f"{start_date} to {end_date}",
"invoices": result[0],
"total_revenue": result[1],
"collected": result[2],
"outstanding": result[3],
"collection_rate": (result[2] / result[1] * 100) if result[1] > 0 else 0
}
# Usage example
if __name__ == "__main__":
fa = FreelanceAutomation()
# Add a client
client_id = fa.add_client("Acme Corp", "billing@acme.com", "Acme Corporation", 150.0)
# Start a project
project_id = fa.start_project(client_id, "Web App Development", budget=15000)
# Log some time
fa.log_time(project_id, 4.5, "Set up project structure and CI/CD")
fa.log_time(project_id, 3.0, "Implemented authentication module")
# Generate an invoice
invoice = fa.generate_invoice(project_id)
print(f"Invoice: ${invoice['amount']:.2f} for {invoice['hours']}h")
Key Features
- Automatic Proposal Generation: Uses templates to create professional proposals
- Time Tracking: Simple logging with billable/non-billable distinction
- Invoice Generation: Creates invoices from unbilled hours
- Revenue Reporting: Track earnings over any time period
- Overdue Alerts: Get notified about unpaid invoices
Extending the System
- Add email integration for automatic proposal sending
- Connect Stripe for payment processing
- Add crypto payment support (BTC/XMR)
- Build a web dashboard for real-time metrics
- Add project milestone tracking
Related Products
Looking for a production-ready version? We have complete Python automation tools at our store, including:
- Invoice generators with crypto payment support
- Client management systems with email integration
- Time tracking dashboards with analytics
Top comments (0)