Project 5: Automated Invoice Generator with Email Delivery
Project 5: Automated Invoice Generator with Email Delivery
Build a system that generates personalized invoices in Excel and emails them to customers automatically.
Project Overview
Your company sends monthly invoices to hundreds of customers. Currently, someone manually fills in an Excel template for each customer, saves it as PDF, and emails it. Your task: automate the entire workflow.
What Youโll Build
- Read customer data and billing details from a database or Excel file
- For each customer, generate a personalized invoice using an Excel template
- Populate invoice with customer-specific data (name, items, amounts, taxes)
- Apply business logic (discounts, tax calculations, late fees)
- Save each invoice as a PDF
- Send invoices via email with personalized messages
- Generate a summary report of all invoices sent
Technologies
openpyxlfor Excel template manipulationxlsxwriteroropenpyxlfor invoice generation- PDF generation (Excelโs built-in PDF export or
reportlab) smtplibfor email sendingjinja2for email template rendering (optional)
Learning Objectives
By completing this project, you will:
- Master template-based document generation - Merge data with templates
- Implement business logic - Discounts, taxes, late fees
- Generate PDFs from Excel - Cross-platform considerations
- Send emails with Python - SMTP, attachments, HTML formatting
- Handle batch processing - Progress tracking, error recovery
- Build auditable systems - Logging, reports, verification
Deep Theoretical Foundation
Template-Based Document Generation
Template-based generation separates structure from content, enabling scalable document creation:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ TEMPLATE-BASED GENERATION PATTERN โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ TEMPLATE (Structure) โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ YOUR COMPANY NAME โโ โ
โ โ โ โโ โ
โ โ โ Invoice #: {{invoice_number}} โโ โ
โ โ โ Date: {{invoice_date}} โโ โ
โ โ โ โโ โ
โ โ โ Bill To: โโ โ
โ โ โ {{customer_name}} โโ โ
โ โ โ {{customer_address}} โโ โ
โ โ โ โโ โ
โ โ โ {{#line_items}} โโ โ
โ โ โ {{description}} | {{qty}} | {{price}} โโ โ
โ โ โ {{/line_items}} โโ โ
โ โ โ โโ โ
โ โ โ Total: {{total}} โโ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ + โ
โ DATA (Content) โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ { โ โ
โ โ "invoice_number": "INV-2024-12-001", โ โ
โ โ "invoice_date": "December 31, 2024", โ โ
โ โ "customer_name": "Acme Corporation", โ โ
โ โ "customer_address": "123 Main St", โ โ
โ โ "line_items": [ โ โ
โ โ {"description": "Widget", "qty": 5, "price": 100},โ โ
โ โ {"description": "Gadget", "qty": 3, "price": 50} โ โ
โ โ ], โ โ
โ โ "total": 650 โ โ
โ โ } โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ = โ
โ OUTPUT (Merged Document) โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ YOUR COMPANY NAME โโ โ
โ โ โ โโ โ
โ โ โ Invoice #: INV-2024-12-001 โโ โ
โ โ โ Date: December 31, 2024 โโ โ
โ โ โ โโ โ
โ โ โ Bill To: โโ โ
โ โ โ Acme Corporation โโ โ
โ โ โ 123 Main St โโ โ
โ โ โ โโ โ
โ โ โ Widget | 5 | $100 โโ โ
โ โ โ Gadget | 3 | $50 โโ โ
โ โ โ โโ โ
โ โ โ Total: $650 โโ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ

Business Logic: Invoice Calculations
Invoice calculations follow a specific order of operations:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ INVOICE CALCULATION FLOW โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ LINE ITEMS โ
โ โโโโโโโโโโโ โ
โ Item 1: Widget ร 50 units ร $125.00 = $6,250.00 โ
โ Item 2: Gadget ร 30 units ร $89.50 = $2,685.00 โ
โ Item 3: Tool ร 75 units ร $67.30 = $5,047.50 โ
โ โโโโโโโโโโโโโ โ
โ SUBTOTAL: $13,982.50 โ
โ โ
โ DISCOUNT (Applied first) โ
โ โโโโโโโโ โ
โ Customer discount rate: 10% โ
โ Discount amount: $13,982.50 ร 0.10 = $1,398.25 โ
โ โโโโโโโโโโโโโ โ
โ SUBTOTAL AFTER DISCOUNT: $12,584.25 โ
โ โ
โ TAX (Applied to discounted subtotal) โ
โ โโโโ โ
โ Tax rate: 9% โ
โ Tax amount: $12,584.25 ร 0.09 = $1,132.58 โ
โ โ
โ LATE FEE (If applicable) โ
โ โโโโโโโโ โ
โ Previous invoice overdue: Yes โ
โ Late fee: 1% of previous balance = $87.50 โ
โ โ
โ FINAL CALCULATION โ
โ โโโโโโโโโโโโโโโโโ โ
โ Subtotal after discount: $12,584.25 โ
โ Tax: + $1,132.58 โ
โ Late fee: + $87.50 โ
โ โโโโโโโโโโโโโ โ
โ TOTAL DUE: $13,804.33 โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ

Email Architecture: SMTP Deep Dive
Understanding email protocols is essential for reliable delivery:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ EMAIL SENDING ARCHITECTURE โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ YOUR PYTHON SCRIPT โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ โ
โ โ smtplib.SMTP('smtp.gmail.com', 587) โ โ
โ โ server.starttls() # Upgrade to secure connection โ โ
โ โ server.login(username, password) โ โ
โ โ server.send_message(msg) โ โ
โ โ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โผ โ
โ YOUR EMAIL PROVIDER (e.g., Gmail) โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ โ
โ โ SMTP Server: smtp.gmail.com:587 โ โ
โ โ โ โ
โ โ Authentication check: โ โ
โ โ โ Valid username/password or app password โ โ
โ โ โ 2FA compliance (if required) โ โ
โ โ โ โ
โ โ Spam checks: โ โ
โ โ โ SPF record valid? โ โ
โ โ โ DKIM signature? โ โ
โ โ โ Rate limiting (max emails/hour) โ โ
โ โ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โผ โ
โ RECIPIENT'S EMAIL SERVER โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ โ
โ โ Receiving checks: โ โ
โ โ โข Is sender domain legitimate? โ โ
โ โ โข Spam score acceptable? โ โ
โ โ โข Virus scan passed? โ โ
โ โ โ โ
โ โ Delivery: โ โ
โ โ โ Inbox (if checks pass) โ โ
โ โ โ Spam folder (if suspicious) โ โ
โ โ โ Rejected (if blacklisted) โ โ
โ โ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โผ โ
โ RECIPIENT'S INBOX โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ โ
โ โ From: billing@yourcompany.com โ โ
โ โ Subject: Your December 2024 Invoice - $13,804.33 โ โ
โ โ Attachment: Invoice_INV-2024-12-001.pdf โ โ
โ โ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ

MIME Message Structure
Email attachments require understanding MIME (Multipurpose Internet Mail Extensions):
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ MIME MESSAGE STRUCTURE โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ MIMEMultipart (Container) โ
โ โโโ Headers โ
โ โ โโโ From: billing@yourcompany.com โ
โ โ โโโ To: customer@client.com โ
โ โ โโโ Subject: Your Invoice โ
โ โ โโโ Content-Type: multipart/mixed โ
โ โ โ
โ โโโ Part 1: MIMEText (alternative) โ
โ โ โโโ text/plain version โ
โ โ โ "Dear John, Please find attached..." โ
โ โ โโโ text/html version (optional) โ
โ โ "<html><body><h1>Invoice</h1>..." โ
โ โ โ
โ โโโ Part 2: MIMEBase (attachment) โ
โ โโโ Content-Type: application/pdf โ
โ โโโ Content-Disposition: attachment; filename="invoice.pdf"โ
โ โโโ Content (base64 encoded PDF bytes) โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
PDF Generation Strategies
There are multiple approaches to generating PDFs from Excel:
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ PDF GENERATION STRATEGIES โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ STRATEGY 1: Excel's Native PDF Export (Windows + Excel) โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ Pros: โ
โ โ Perfect fidelity - exactly matches Excel view โ
โ โ Supports all Excel features (charts, formatting) โ
โ โ Simple API via win32com โ
โ Cons: โ
โ โ Requires Excel installed โ
โ โ Windows only (or Mac with modifications) โ
โ โ Slower (launches Excel process) โ
โ โ
โ Code: โ
โ excel = win32com.client.Dispatch("Excel.Application") โ
โ wb = excel.Workbooks.Open(xlsx_path) โ
โ wb.ExportAsFixedFormat(0, pdf_path) # 0 = PDF โ
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ STRATEGY 2: LibreOffice Headless (Cross-platform) โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ Pros: โ
โ โ Free and open source โ
โ โ Works on Linux, Mac, Windows โ
โ โ Good Excel compatibility โ
โ Cons: โ
โ โ Requires LibreOffice installed โ
โ โ Some formatting may differ โ
โ โ
โ Code: โ
โ subprocess.run(['libreoffice', '--headless', โ
โ '--convert-to', 'pdf', xlsx_path]) โ
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ STRATEGY 3: Generate PDF Directly (No Excel) โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ Pros: โ
โ โ No external dependencies โ
โ โ Full control over output โ
โ โ Works anywhere Python runs โ
โ Cons: โ
โ โ Must recreate layout in code โ
โ โ More code to write โ
โ โ
โ Libraries: reportlab, weasyprint, fpdf โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
The Core Question Youโre Answering
โHow do I transform data into personalized, professional documents at scale?โ
This is template-based document automationโa foundational pattern in business automation. Youโre learning to separate structure (the template) from content (the data), then merge them programmatically.
Project Specification
Input Requirements
- Customer/Billing Data:
customer_billing.xlsxor databasecustomer_id | name | address | city | state | zip | email | discount_rate | payment_terms - Line Items Data: Transactions for the billing period
customer_id | product | quantity | unit_price | date - Invoice Template:
invoice_template.xlsx- Pre-designed layout with placeholder cells
- Formatting, logos, and structure already defined
Output Requirements
- Individual Invoices (per customer):
- Excel file:
invoices/2024-12/{Customer}_INV-YYYY-MM-XXX.xlsx - PDF file:
invoices/2024-12/{Customer}_INV-YYYY-MM-XXX.pdf
- Excel file:
- Email Sent to each customer with:
- Personalized subject line
- Professional body text with invoice summary
- PDF attachment
- Summary Report:
Invoice_Summary_YYYY-MM.xlsx- List of all invoices generated
- Total revenue
- Email delivery status
- Any errors or flagged items
Functional Requirements
- Support variable number of line items (1-100+)
- Apply customer-specific discount rates
- Calculate tax based on location
- Add late fees for overdue accounts
- Handle email failures gracefully (retry, log, continue)
- Idempotent: safe to run multiple times for same period
- Generate unique sequential invoice numbers
Solution Architecture
Component Design
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ INVOICE AUTOMATION SYSTEM โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ DATA LAYER โ โ
โ โ โ โ
โ โ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโ โ โ
โ โ โ CustomerDB โ โ LineItems โ โ InvoiceNum โ โ โ
โ โ โ โ โ โ โ Sequence โ โ โ
โ โ โ โข get_all()โ โ โข by_cust()โ โ โข next() โ โ โ
โ โ โ โข by_id() โ โ โข by_date()โ โ โข reserve()โ โ โ
โ โ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โผ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ BUSINESS LOGIC LAYER โ โ
โ โ โ โ
โ โ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโ โ โ
โ โ โ Calculator โ โ Validator โ โ LateFee โ โ โ
โ โ โ โ โ โ โ Checker โ โ โ
โ โ โ โข subtotal โ โ โข address โ โ โข is_late()โ โ โ
โ โ โ โข discount โ โ โข email โ โ โข calc_fee() โ โ
โ โ โ โข tax โ โ โข items โ โ โ โ โ
โ โ โ โข total โ โ โ โ โ โ โ
โ โ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โผ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ GENERATION LAYER โ โ
โ โ โ โ
โ โ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโ โ โ
โ โ โ Template โ โ PDF โ โ Email โ โ โ
โ โ โ Engine โ โ Generator โ โ Sender โ โ โ
โ โ โ โ โ โ โ โ โ โ
โ โ โ โข load() โ โ โข convert()โ โ โข compose()โ โ โ
โ โ โ โข fill() โ โ โข save() โ โ โข attach() โ โ โ
โ โ โ โข save() โ โ โ โ โข send() โ โ โ
โ โ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โผ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ REPORTING LAYER โ โ
โ โ โ โ
โ โ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโ โ โ
โ โ โ Summary โ โ Logger โ โ Error โ โ โ
โ โ โ Report โ โ โ โ Handler โ โ โ
โ โ โ โ โ โข info() โ โ โ โ โ
โ โ โ โข create() โ โ โข warn() โ โ โข capture()โ โ โ
โ โ โ โข add_row()โ โ โข error() โ โ โข retry() โ โ โ
โ โ โ โข save() โ โ โ โ โข escalate() โ โ
โ โ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโ โโโโโโโโโโโโโโ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Data Flow
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ COMPLETE DATA FLOW โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ Phase 1: INITIALIZATION โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ 1. Load configuration (SMTP settings, paths, tax rates)โ โ
โ โ 2. Connect to data sources โ โ
โ โ 3. Load invoice template โ โ
โ โ 4. Initialize invoice number sequence โ โ
โ โ 5. Create output directories โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โผ โ
โ Phase 2: DATA GATHERING โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ 1. Fetch all active customers โ โ
โ โ 2. For each customer: โ โ
โ โ a. Get line items for billing period โ โ
โ โ b. Check for overdue balances โ โ
โ โ c. Get customer-specific settings โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โผ โ
โ Phase 3: PER-CUSTOMER PROCESSING (Loop) โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ For each customer with line items: โ โ
โ โ โ โ
โ โ 3a. CALCULATE โ โ
โ โ - Subtotal = sum(quantity ร unit_price) โ โ
โ โ - Discount = subtotal ร discount_rate โ โ
โ โ - Tax = (subtotal - discount) ร tax_rate โ โ
โ โ - Late fee (if applicable) โ โ
โ โ - Total = subtotal - discount + tax + late_fee โ โ
โ โ โ โ
โ โ 3b. GENERATE INVOICE โ โ
โ โ - Load template copy โ โ
โ โ - Fill customer info cells โ โ
โ โ - Fill line items rows โ โ
โ โ - Fill totals cells โ โ
โ โ - Save as .xlsx โ โ
โ โ โ โ
โ โ 3c. CONVERT TO PDF โ โ
โ โ - Open .xlsx with Excel/LibreOffice โ โ
โ โ - Export as PDF โ โ
โ โ - Close and save โ โ
โ โ โ โ
โ โ 3d. SEND EMAIL โ โ
โ โ - Compose email with customer name โ โ
โ โ - Attach PDF โ โ
โ โ - Send via SMTP โ โ
โ โ - Log result (success/failure) โ โ
โ โ โ โ
โ โ 3e. RECORD IN SUMMARY โ โ
โ โ - Add row to summary report โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โผ โ
โ Phase 4: FINALIZATION โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ 1. Save summary report โ โ
โ โ 2. Save error log (if any failures) โ โ
โ โ 3. Print completion statistics โ โ
โ โ 4. Send admin notification (optional) โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Invoice Template Structure
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ TEMPLATE CELL MAPPING โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ TEMPLATE CELL PLACEHOLDER DATA SOURCE โ
โ โโโโโโโโโโโโ โโโโโโโโโโโ โโโโโโโโโโโ โ
โ B2 COMPANY_LOGO Static (in template) โ
โ B4 COMPANY_NAME Static โ
โ B5 COMPANY_ADDRESS Static โ
โ B6 COMPANY_PHONE Static โ
โ โ
โ G4 {{invoice_number}} Generated sequence โ
โ G5 {{invoice_date}} datetime.now() โ
โ G6 {{due_date}} invoice_date + 30 days โ
โ โ
โ B10 {{customer_name}} customers.name โ
โ B11 {{customer_address}} customers.address โ
โ B12 {{customer_city_st_zip}} Formatted โ
โ B13 {{customer_email}} customers.email โ
โ โ
โ A17:E17+ LINE_ITEM_ROWS line_items[] โ
โ A Row number Auto-generated โ
โ B Description line_items.product โ
โ C Quantity line_items.quantity โ
โ D Unit Price line_items.unit_price โ
โ E Amount qty ร unit_price โ
โ โ
โ E47 {{subtotal}} sum(line amounts) โ
โ E48 {{discount}} subtotal ร rate โ
โ E49 {{subtotal_after}} subtotal - discount โ
โ E50 {{tax}} subtotal_after ร tax_rateโ
โ E51 {{late_fee}} if overdue: fee amount โ
โ E53 {{total_due}} Final total โ
โ โ
โ B55 {{payment_terms}} customers.payment_terms โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Phased Implementation Guide
Phase 1: Template Design
Goal: Create a professional invoice template in Excel.
Tasks:
- Design invoice layout in Excel
- Add company branding (logo, colors)
- Mark placeholder cells with identifiable patterns
- Set print area and page layout
- Test that it looks correct when opened
Checkpoint: Template opens correctly and looks professional.
Phase 2: Data Loading
Goal: Load customer and billing data into Python.
Tasks:
- Read customer data from Excel/database
- Read line items for billing period
- Group line items by customer
- Validate required fields exist
- Handle missing/invalid data
Checkpoint: Dictionary of customer โ line_items ready.
Phase 3: Invoice Calculation Logic
Goal: Implement accurate business calculations.
Tasks:
- Calculate line item amounts (qty ร price)
- Calculate subtotal
- Apply customer discount rate
- Calculate tax based on location
- Check for and apply late fees
- Calculate final total
Checkpoint: All calculations match manual Excel verification.
Phase 4: Template Filling
Goal: Populate template with one customerโs data.
Tasks:
- Load template as new workbook
- Write customer information
- Write line items (handling variable count)
- Write calculated totals
- Generate invoice number
- Save with customer-specific filename
Checkpoint: Single complete invoice generates correctly.
Phase 5: PDF Conversion
Goal: Convert Excel invoices to PDF.
Tasks:
- Choose PDF generation strategy
- Implement conversion function
- Handle page breaks for long invoices
- Verify PDF matches Excel formatting
- Save PDF alongside Excel file
Checkpoint: PDFs generate and look correct.
Phase 6: Email Composition
Goal: Create professional invoice emails.
Tasks:
- Create email template (plain text and HTML)
- Compose personalized subject line
- Build email body with invoice summary
- Attach PDF
- Test email locally (print to console)
Checkpoint: Email content is correct without actually sending.
Phase 7: Email Sending
Goal: Actually send emails via SMTP.
Tasks:
- Configure SMTP connection
- Handle authentication (app passwords)
- Send test email to yourself
- Implement retry logic for failures
- Log send results
Checkpoint: Emails arrive in inboxes with attachments.
Phase 8: Batch Processing
Goal: Process all customers efficiently.
Tasks:
- Implement main loop over customers
- Add progress tracking (tqdm)
- Handle per-customer errors (continue, donโt crash)
- Collect all results for summary
- Implement rate limiting for email
Checkpoint: Full run processes all customers.
Phase 9: Summary Report
Goal: Generate audit trail and summary.
Tasks:
- Track all generated invoices
- Track all email send results
- Calculate total revenue
- Save summary to Excel
- Highlight any failures
Checkpoint: Summary accurately reflects run results.
Testing Strategy
Unit Tests
def test_calculate_subtotal():
items = [
{'quantity': 5, 'unit_price': 100},
{'quantity': 3, 'unit_price': 50}
]
assert calculate_subtotal(items) == 650
def test_apply_discount():
subtotal = 1000
discount_rate = 0.10
result = apply_discount(subtotal, discount_rate)
assert result == 900
def test_calculate_tax():
amount = 900
tax_rate = 0.09
tax = calculate_tax(amount, tax_rate)
assert tax == 81
def test_invoice_number_sequence():
gen = InvoiceNumberGenerator('2024-12')
assert gen.next() == 'INV-2024-12-001'
assert gen.next() == 'INV-2024-12-002'
Integration Tests
def test_full_invoice_generation():
customer = {
'name': 'Test Customer',
'email': 'test@example.com',
'address': '123 Test St',
'discount_rate': 0.10
}
items = [
{'product': 'Widget', 'quantity': 5, 'unit_price': 100}
]
invoice_path = generate_invoice(customer, items, 'INV-TEST-001')
assert os.path.exists(invoice_path)
wb = load_workbook(invoice_path)
ws = wb.active
assert ws['B10'].value == 'Test Customer'
assert ws['E47'].value == 500 # subtotal
def test_pdf_generation():
xlsx_path = 'test_invoice.xlsx'
pdf_path = convert_to_pdf(xlsx_path)
assert os.path.exists(pdf_path)
assert pdf_path.endswith('.pdf')
def test_email_composition():
msg = compose_invoice_email(
customer_email='test@example.com',
customer_name='John',
invoice_number='INV-001',
amount=1500.00,
pdf_path='test.pdf'
)
assert msg['To'] == 'test@example.com'
assert 'INV-001' in msg['Subject']
assert len(msg.get_payload()) == 2 # body + attachment
Manual Testing Checklist
- Invoice layout matches design
- All placeholders filled correctly
- Calculations are accurate
- PDF looks identical to Excel
- Email subject is personalized
- Email body contains correct amounts
- PDF attachment opens correctly
- Email doesnโt go to spam
- Summary report is accurate
- Error handling works (test with invalid email)
Common Pitfalls and Debugging
Pitfall 1: Email Goes to Spam
Symptom: Emails sent but not appearing in inbox.
Causes and solutions:
- Sender reputation: Use a reputable email service
- Missing SPF/DKIM: Configure domain DNS properly
- Suspicious subject: Avoid all-caps, โFREEโ, urgency words
- Too many recipients: Batch slowly, respect rate limits
- Plain text only: Include HTML version too
Pitfall 2: PDF Formatting Different from Excel
Symptom: PDF has different margins, page breaks, fonts.
Cause: Print settings not configured in template.
Solution:
- In template: Set Page Layout โ Print Area
- Set Page Layout โ Margins
- Set Page Layout โ Page Breaks
- Test print preview before automating
Pitfall 3: Line Items Overflow
Symptom: Customer with 50 items breaks the template.
Cause: Fixed row count in template.
Solution:
def insert_line_items(ws, items, start_row):
# Insert rows if needed
if len(items) > MAX_TEMPLATE_ROWS:
extra_rows = len(items) - MAX_TEMPLATE_ROWS
ws.insert_rows(start_row + MAX_TEMPLATE_ROWS, extra_rows)
# Copy formatting from template row
template_row = ws[start_row]
for i, item in enumerate(items):
target_row = start_row + i
# Apply template formatting
copy_row_formatting(template_row, ws[target_row])
# Fill data
ws[f'A{target_row}'] = i + 1
ws[f'B{target_row}'] = item['description']
# ...
Pitfall 4: SMTP Authentication Failure
Symptom: โAuthentication failedโ error.
Cause: Gmail/Outlook requires app-specific passwords.
Solution:
- For Gmail: Enable 2FA, create App Password
- For Outlook: Create app password or use OAuth
- Never use main account password in code
Pitfall 5: Memory Error on Large Batches
Symptom: Script crashes processing 500+ customers.
Cause: Keeping all invoices in memory.
Solution:
# Bad: Load all at once
all_invoices = [generate_invoice(c) for c in customers]
# Good: Process and release
for customer in customers:
invoice = generate_invoice(customer)
send_email(customer, invoice)
# invoice is garbage collected here
Extensions and Challenges
Extension 1: Customer Portal Integration
Generate unique payment links for each invoice.
Skills practiced: URL generation, web integration
Extension 2: Payment Tracking
Update invoice status when payment received.
Skills practiced: State management, database updates, webhooks
Extension 3: Multi-Currency Support
Generate invoices in customerโs local currency.
Skills practiced: Currency conversion APIs, formatting
Extension 4: Recurring Invoices
Automatically generate invoices on schedule.
Skills practiced: Scheduling, subscription management
Extension 5: Invoice Templates per Customer
Different customers get different template designs.
Skills practiced: Template selection logic, customization
Real-World Connections
Industry Applications
- SaaS Companies: Monthly subscription invoices
- Consulting Firms: Project billing and time tracking
- Manufacturing: Order confirmations and invoices
- Freelancers: Client billing automation
Career Relevance
This project demonstrates skills for:
- Automation Engineer roles
- Business Analyst positions
- Backend Developer roles
- DevOps Engineer (for scheduling/deployment)
Portfolio Value
When presenting:
- Show the volume handled (247 invoices in 4 minutes)
- Demonstrate error recovery
- Explain the template system flexibility
- Discuss security (credentials, customer data)
Interview Questions
Prepare to answer:
-
โHow would you handle invoices that need to be generated in multiple languages?โ
-
โWhatโs the best way to prevent email from being marked as spam?โ
-
โHow do you securely store SMTP credentials?โ
-
โWhat if a customer needs a correction to an already-sent invoice?โ
-
โHow would you scale this to handle 10,000 invoices instead of 247?โ
-
โWhatโs the difference between generating Excel then converting to PDF vs generating PDF directly?โ
Hints by Layer
Layer 1: Load Template and Customer Data
from openpyxl import load_workbook
import pandas as pd
# Load template
template = load_workbook('invoice_template.xlsx')
# Load customer data
customers = pd.read_excel('customer_billing_data.xlsx')
Layer 2: Fill Template for One Customer
def generate_invoice(customer_data, items, invoice_num):
wb = load_workbook('invoice_template.xlsx')
ws = wb.active
# Fill in customer data
ws['G4'] = f'INV-2024-12-{invoice_num:03d}'
ws['G5'] = datetime.now().strftime('%B %d, %Y')
ws['G6'] = (datetime.now() + timedelta(days=30)).strftime('%B %d, %Y')
ws['B10'] = customer_data['name']
ws['B11'] = customer_data['address']
ws['B12'] = f"{customer_data['city']}, {customer_data['state']} {customer_data['zip']}"
# Fill line items
row = 17
subtotal = 0
for item in items:
ws[f'A{row}'] = row - 16
ws[f'B{row}'] = item['product']
ws[f'C{row}'] = item['quantity']
ws[f'D{row}'] = item['unit_price']
amount = item['quantity'] * item['unit_price']
ws[f'E{row}'] = amount
subtotal += amount
row += 1
# Calculate totals
discount = subtotal * customer_data['discount_rate']
subtotal_after = subtotal - discount
tax = subtotal_after * 0.09
total = subtotal_after + tax
ws['E47'] = subtotal
ws['E48'] = -discount
ws['E50'] = tax
ws['E53'] = total
filename = f"invoices/{customer_data['name'].replace(' ', '_')}_INV-{invoice_num:03d}.xlsx"
wb.save(filename)
return filename, total
Layer 3: Convert to PDF (Windows)
import win32com.client
def excel_to_pdf(excel_path):
pdf_path = excel_path.replace('.xlsx', '.pdf')
excel = win32com.client.Dispatch("Excel.Application")
excel.Visible = False
try:
wb = excel.Workbooks.Open(os.path.abspath(excel_path))
wb.ExportAsFixedFormat(0, os.path.abspath(pdf_path))
wb.Close(False)
finally:
excel.Quit()
return pdf_path
Layer 4: Send Email with Attachment
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
def send_invoice_email(customer_email, customer_name, pdf_path, amount):
sender = 'billing@yourcompany.com'
password = 'your_app_password' # Use environment variable in production!
msg = MIMEMultipart()
msg['From'] = sender
msg['To'] = customer_email
msg['Subject'] = f'Your December 2024 Invoice - ${amount:,.2f}'
body = f"""Dear {customer_name},
Thank you for your continued business.
Please find attached your invoice for December 2024.
Amount Due: ${amount:,.2f}
Best regards,
Your Company Billing Team
"""
msg.attach(MIMEText(body, 'plain'))
# Attach PDF
with open(pdf_path, '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={os.path.basename(pdf_path)}')
msg.attach(part)
# Send
with smtplib.SMTP('smtp.gmail.com', 587) as server:
server.starttls()
server.login(sender, password)
server.send_message(msg)
Layer 5: Batch Processing with Progress
from tqdm import tqdm
def process_all_invoices(customers, items_by_customer):
results = []
failures = []
for idx, customer in tqdm(customers.iterrows(), total=len(customers)):
try:
items = items_by_customer.get(customer['id'], [])
if not items:
continue # Skip customers with no items
# Generate invoice
excel_path, total = generate_invoice(customer, items, idx + 1)
pdf_path = excel_to_pdf(excel_path)
# Send email
send_invoice_email(customer['email'], customer['name'], pdf_path, total)
results.append({
'customer': customer['name'],
'invoice': os.path.basename(excel_path),
'amount': total,
'status': 'Sent'
})
except Exception as e:
failures.append({
'customer': customer['name'],
'error': str(e)
})
return results, failures
Self-Assessment Checklist
Before considering complete:
- Template designed and professional-looking
- Customer data loads correctly
- Line items grouped by customer
- Calculations verified accurate
- Invoices generate for all customers
- PDFs match Excel formatting
- Emails composed with correct content
- Emails send successfully
- Attachments open correctly
- Summary report accurate
- Errors handled gracefully
- Credentials stored securely (not in code)
- Script is idempotent (safe to rerun)
- Performance acceptable (< 5 min for 250 customers)
- Logging provides audit trail
Resources
| Topic | Resource |
|---|---|
| Excel automation with openpyxl | โAutomate the Boring Stuff with Pythonโ by Al Sweigart - Ch. 13 |
| Email automation | โAutomate the Boring Stuff with Pythonโ by Al Sweigart - Ch. 18 |
| PDF generation | โAutomate the Boring Stuff with Pythonโ by Al Sweigart - Ch. 15 |
| Template pattern | โDesign Patternsโ by Gang of Four |
| Business logic design | โClean Codeโ by Robert C. Martin - Ch. 6 |
| Error handling and logging | โFluent Pythonโ by Luciano Ramalho - Ch. 7 |