How to Build an AI Invoice Generator
Generate and send professional invoices automatically from project data.
Jay Banlasan
The AI Systems Guy
Creating invoices manually from project data is tedious and error-prone. I built an ai invoice generator for automated billing that pulls hours, expenses, and deliverables from your project tracking system and produces ready-to-send invoices. No spreadsheet wrangling.
Data flows from project to invoice without human copying.
What You Need Before Starting
- Python 3.8+
- Time tracking or project data source
- Invoice template (HTML or PDF)
- Email sending for delivery
Step 1: Define Invoice Data Structure
from datetime import datetime
from dataclasses import dataclass, field
@dataclass
class InvoiceItem:
description: str
quantity: float
rate: float
amount: float = 0
def __post_init__(self):
self.amount = round(self.quantity * self.rate, 2)
@dataclass
class Invoice:
invoice_number: str
client_name: str
client_email: str
items: list = field(default_factory=list)
due_date: str = ""
notes: str = ""
@property
def subtotal(self):
return round(sum(item.amount for item in self.items), 2)
@property
def tax(self):
return round(self.subtotal * 0.0, 2)
@property
def total(self):
return round(self.subtotal + self.tax, 2)
Step 2: Pull Data from Time Tracking
def build_invoice_from_project(project_data, client_info, invoice_number):
items = []
for entry in project_data["time_entries"]:
items.append(InvoiceItem(
description=f"{entry['task']} ({entry['date']})",
quantity=entry["hours"],
rate=client_info["hourly_rate"]
))
for expense in project_data.get("expenses", []):
items.append(InvoiceItem(
description=f"Expense: {expense['description']}",
quantity=1,
rate=expense["amount"]
))
due_date = (datetime.now() + __import__('datetime').timedelta(days=30)).strftime("%Y-%m-%d")
return Invoice(
invoice_number=invoice_number,
client_name=client_info["name"],
client_email=client_info["email"],
items=items,
due_date=due_date
)
Step 3: Generate Invoice HTML
def generate_invoice_html(invoice):
items_html = ""
for item in invoice.items:
items_html += f"""
<tr>
<td>{item.description}</td>
<td>{item.quantity}</td>
<td>${item.rate:.2f}</td>
<td>${item.amount:.2f}</td>
</tr>"""
return f"""<!DOCTYPE html>
<html>
<head><style>
body {{ font-family: Arial, sans-serif; margin: 40px; }}
table {{ width: 100%; border-collapse: collapse; margin-top: 20px; }}
th, td {{ padding: 10px; text-align: left; border-bottom: 1px solid #ddd; }}
.total {{ font-weight: bold; font-size: 1.2em; }}
</style></head>
<body>
<h1>Invoice #{invoice.invoice_number}</h1>
<p>Bill to: {invoice.client_name}</p>
<p>Date: {datetime.now().strftime('%B %d, %Y')}</p>
<p>Due: {invoice.due_date}</p>
<table>
<tr><th>Description</th><th>Qty</th><th>Rate</th><th>Amount</th></tr>
{items_html}
</table>
<p class="total">Total: ${invoice.total:.2f}</p>
<p>{invoice.notes}</p>
</body></html>"""
Step 4: Convert to PDF
def save_invoice_pdf(html_content, output_path):
try:
from weasyprint import HTML
HTML(string=html_content).write_pdf(output_path)
except ImportError:
from pathlib import Path
Path(output_path.replace('.pdf', '.html')).write_text(html_content)
return output_path
Step 5: Send the Invoice
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 send_invoice(invoice, pdf_path, smtp_config):
msg = MIMEMultipart()
msg["Subject"] = f"Invoice #{invoice.invoice_number} - {invoice.client_name}"
msg["To"] = invoice.client_email
body = f"Hi {invoice.client_name},\n\nPlease find attached invoice #{invoice.invoice_number} for ${invoice.total:.2f}.\n\nDue date: {invoice.due_date}\n\nThank you."
msg.attach(MIMEText(body))
with open(pdf_path, "rb") as f:
attachment = MIMEBase("application", "pdf")
attachment.set_payload(f.read())
encoders.encode_base64(attachment)
attachment.add_header("Content-Disposition", f"attachment; filename=invoice-{invoice.invoice_number}.pdf")
msg.attach(attachment)
with smtplib.SMTP(smtp_config["host"], smtp_config["port"]) as server:
server.starttls()
server.login(smtp_config["user"], smtp_config["password"])
server.send_message(msg)
print(f"Invoice #{invoice.invoice_number} sent to {invoice.client_email}")
What to Build Next
Add payment tracking that matches incoming payments to invoices. When a payment clears, the invoice status updates automatically and triggers a thank-you email. Generation is step one. Collection is where the money moves.
Related Reading
- Building Your First Automation: A Complete Guide - automation fundamentals
- Cost of Manual vs Cost of Automated - the cost of manual invoicing
- How to Audit Your Operations for AI Opportunities - finding billing automation opportunities
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