Systems Library / Finance Automation / How to Automate Recurring Invoice Creation
Finance Automation invoicing billing

How to Automate Recurring Invoice Creation

Set up recurring invoices that generate and send on schedule.

Jay Banlasan

Jay Banlasan

The AI Systems Guy

Retainer clients get the same invoice every month. That should not require manual work. I built a system to automate recurring invoice creation that generates and sends invoices on schedule, handles different billing cycles, and tracks payment status for each one.

Set it once. It bills on autopilot.

What You Need Before Starting

Step 1: Define Recurring Billing Configs

RECURRING_CLIENTS = [
    {
        "client_id": "acme-001",
        "name": "Acme Corp",
        "email": "[email protected]",
        "amount": 5000,
        "description": "Monthly retainer - AI operations management",
        "frequency": "monthly",
        "billing_day": 1,
        "payment_terms_days": 30
    },
    {
        "client_id": "beta-002",
        "name": "Beta Inc",
        "email": "[email protected]",
        "amount": 2500,
        "description": "Monthly retainer - Ad operations",
        "frequency": "monthly",
        "billing_day": 15,
        "payment_terms_days": 15
    }
]

Step 2: Check What Needs Billing Today

from datetime import datetime, timedelta

def get_due_invoices(clients):
    today = datetime.now()
    due = []

    for client in clients:
        if client["frequency"] == "monthly" and today.day == client["billing_day"]:
            due.append(client)
        elif client["frequency"] == "weekly" and today.weekday() == 0:
            due.append(client)
        elif client["frequency"] == "quarterly":
            quarter_months = [1, 4, 7, 10]
            if today.month in quarter_months and today.day == client["billing_day"]:
                due.append(client)

    return due

Step 3: Generate Invoice Numbers

import sqlite3

def init_invoice_db(db_path="invoices.db"):
    conn = sqlite3.connect(db_path)
    conn.execute("""
        CREATE TABLE IF NOT EXISTS invoices (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            invoice_number TEXT UNIQUE,
            client_id TEXT,
            amount REAL,
            status TEXT DEFAULT 'sent',
            created_at TEXT,
            due_date TEXT,
            paid_at TEXT
        )
    """)
    conn.commit()
    return conn

def next_invoice_number(conn):
    row = conn.execute("SELECT MAX(id) FROM invoices").fetchone()
    next_id = (row[0] or 0) + 1
    year = datetime.now().strftime("%Y")
    return f"INV-{year}-{next_id:04d}"

Step 4: Generate and Send

def create_recurring_invoice(conn, client):
    invoice_num = next_invoice_number(conn)
    due_date = (datetime.now() + timedelta(days=client["payment_terms_days"])).strftime("%Y-%m-%d")

    conn.execute(
        "INSERT INTO invoices (invoice_number, client_id, amount, created_at, due_date) VALUES (?,?,?,?,?)",
        (invoice_num, client["client_id"], client["amount"],
         datetime.now().isoformat(), due_date)
    )
    conn.commit()

    print(f"Created {invoice_num} for {client['name']}: ${client['amount']} due {due_date}")
    return invoice_num

def run_recurring_billing():
    conn = init_invoice_db()
    due_clients = get_due_invoices(RECURRING_CLIENTS)

    for client in due_clients:
        invoice_num = create_recurring_invoice(conn, client)
        # Use invoice generator from system 341 to create and send PDF

    print(f"Processed {len(due_clients)} recurring invoices")

Step 5: Track Payment Status

def mark_paid(conn, invoice_number):
    conn.execute(
        "UPDATE invoices SET status='paid', paid_at=? WHERE invoice_number=?",
        (datetime.now().isoformat(), invoice_number)
    )
    conn.commit()

def billing_summary(conn):
    total = conn.execute("SELECT SUM(amount) FROM invoices WHERE status='sent'").fetchone()[0] or 0
    paid = conn.execute("SELECT SUM(amount) FROM invoices WHERE status='paid'").fetchone()[0] or 0
    overdue_count = conn.execute(
        "SELECT COUNT(*) FROM invoices WHERE status='sent' AND due_date < ?",
        (datetime.now().strftime("%Y-%m-%d"),)
    ).fetchone()[0]

    return {"outstanding": total, "collected": paid, "overdue_count": overdue_count}

Schedule daily:

0 8 * * * python3 /path/to/recurring_billing.py

What to Build Next

Add automatic rate adjustments for annual renewals. When a contract has a built-in rate increase, the system should update the recurring amount on the anniversary date without manual intervention.

Related Reading

Want this system built for your business?

Get a free assessment. We will map every system your business needs and show you the ROI.

Get Your Free Assessment

Related Systems