Need a stack of PDF files to test your code? Use this Invoice Generator Script to create unlimited dummy pdf files.
This Python utility script generates professional invoices in PDF format using the fpdf
library. Each invoice includes metadata such as invoice number, date, balance due, and a detailed table of line items. Here’s how you can customize the script to control the number of invoices generated and adjust the invoice numbering.
How to Use and Customize
Controlling the Number of Invoices Created
The script generates a default of 20 invoices. You can control this number by modifying the random.choices
call in the following line:
for i, business in enumerate(random.choices(businesses, k=20), start=1):
Steps to Adjust:
Change the value of k
in random.choices(businesses, k=20)
to your desired number of invoices.
For example, to generate 10 invoices:
for i, business in enumerate(random.choices(businesses, k=10), start=1):
For 50 invoices:
for i, business in enumerate(random.choices(businesses, k=50), start=1):
Save and run the script. The specified number of invoices will be created in the invoices
directory.
Changing the Invoice Numbering
Invoice numbers are dynamically generated as random 9-digit numbers in this line:
pdf.cell(50, 8, f"Invoice #: {random.randint(100000000, 999999999)}", ln=True, align='L')
Steps to Customize Invoice Numbers:
- Replace the
random.randint
function with a sequential or fixed pattern.
Example 1: Sequential Invoice Numbers
To use sequential invoice numbers:
pdf.cell(50, 8, f"Invoice #: {index:09d}", ln=True, align='L')
- This will generate sequential invoice numbers like
000000001
,000000002
, etc., based on theindex
.
Example 2: Prefix with Custom Text
To add a prefix like “INV-” to the invoice numbers:
pdf.cell(50, 8, f"Invoice #: INV-{index:06d}", ln=True, align='L')
- This will generate invoice numbers like
INV-000001
,INV-000002
, and so on.
Example 3: Fixed Invoice Numbers
For fixed invoice numbers (e.g., for testing purposes):
pdf.cell(50, 8, "Invoice #: INV-2023001", ln=True, align='L')
Additional Customizations:
- Modify Line Items for Variety:
- Add or remove items in the
line_items
list to tailor the services or products included in the invoices.
- Control Large Invoices:
- By default, 20% of invoices include 25–40 line items. You can adjust this probability or the range:
if random.random() < 0.2: # Probability of large invoices
num_items = random.randint(25, 40)
else:
num_items = random.randint(3, 10)
To make all invoices large, set the condition to always True
:
num_items = random.randint(25, 40)
Output Folder:
- The invoices are saved in the
invoices
directory. Change this path as needed:
Full Script:
Install fpdf
:
pip install fpdf
import os
import random
from fpdf import FPDF
from datetime import datetime, timedelta
# Define the fictitious business names, addresses, emails, and line items
businesses = [
{"name": "CodeCraft Solutions LLC", "address": "101 Innovation Drive, Suite 203, Tech City, CA 94025", "email": "jdesint@codecraftsolutionsllc.com"},
{"name": "Digital Horizon Technologies", "address": "501A Skyline Blvd, Suite 1200, New York, NY 10001", "email": "codeteam@digitalhorizontechnical.com"},
{"name": "PixelPoint Software Inc.", "address": "300 Main Street, Suite 402, Austin, TX 78701", "email": "programming@pixelpointsoftwareinc.com"},
{"name": "Syntax Savvy Systems", "address": "88 Codex Avenue, Boston, MA 02115", "email": "sales@syntaxsavvysys.com"},
{"name": "InnovateLogic Software", "address": "52553 Industrial Parkway, Denver, CO 80203", "email": "billing@innovatelogicsoftware.com"},
{"name": "NeoByte Enterprises", "address": "01 Binary Lane, Silicon Valley, CA 94086-0042", "email": "receivables@neobyteinc.com"},
{"name": "NextGen Software Solutions", "address": "721 Digital Street, Orlando, FL 32801", "email": "jamessmith@nextgensoftc.com"},
{"name": "BlueShift Technologies", "address": "900 Orbit Drive, Suite 7, Chicago, IL 60605", "email": "sales@blueshifttechllc.com"},
{"name": "QuantumByte Systems", "address": "600 Quantum Boulevard, Seattle, WA 98101", "email": "teams@quantumbytesystems.com"},
{"name": "BrightPath Software Labs", "address": "123 Innovation Crescent, San Francisco, CA 94105", "email": "support@brightpysoftware.com"},
{"name": "Python Software Labs", "address": "4453 Python Ave., New Castle, DE 19720-1417", "email": "billing@pythonlabscorp.com"},
{"name": "TechWave Solutions", "address": "456 Technology Park, Suite 300, Boston, MA 02110", "email": "janedoe@ccincsolutions.com"},
{"name": "CodeCrafters Inc.", "address": "789 Innovation Blvd, San Diego, CA 92101", "email": "systems@codecraftersinc.com"},
{"name": "NextLevel Software", "address": "101 Progress Ave, Suite 202, Austin, TX 73301", "email": "support@nextlevelsoftllc.com"},
{"name": "LogicStream Labs", "address": "333 Visionary Lane, Orlando, FL 32819", "email": "accounts@logicstreamlabscode.com"}
]
line_items = [
"Custom API Development",
"Backend Development",
"Backend Optimization",
"Bug Fixing and QA",
"Frontend Testing",
"Backend Testing",
"Deployment Meeting",
"Database Schema Update",
"Django Web Framework Setup",
"Machine Learning Deployment",
"Data Parsing and Cleaning Scripts",
"Python Code Optimization",
"REST API Implementation",
"Cloud Deployment Setup",
"AI Chatbot Development",
"Web Scraping Scripts",
"Automation Script Development",
"Performance Testing and Reports",
"Algorithm Efficiency Testing",
"API Integration Services",
"Cross-Platform Compatibility Testing",
"Software Architecture Design",
"Version Control Management",
"Continuous Integration/Deployment Setup",
"Database Migration",
"Containerization with Docker",
"Kubernetes Cluster Configuration",
"Server Load Testing",
"Microservices Architecture Implementation",
"Codebase Documentation",
"Code Refactoring for Efficiency",
"Integration of Third-Party APIs",
"Custom Plugin Development",
"Software Security Audit",
"End-to-End Encryption Implementation",
"Data Backup and Recovery Automation",
"UX/UI Prototype Development",
"Performance Monitoring Setup",
"Real-Time Data Processing",
"GraphQL API Implementation",
"Mobile Application Debugging",
"Cloud Service Cost Optimization",
"Codebase Dependency Updates"
]
class CustomInvoicePDF(FPDF):
def header(self):
# Add logo
if os.path.exists("logo.png"):
self.image("logo.png", x=10, y=8, w=33) # Place the logo at the top-left corner
# Move the cursor to position the text below the logo
self.set_y(45) # Adjust the vertical position (Y-coordinate)
# Add company details below the logo with tighter spacing
self.set_font("Arial", size=12)
self.cell(0, 6, "Py-Core.com", ln=True, align='L') # Reduce cell height to 6
self.cell(0, 6, "4543 Tech Street", ln=True, align='L')
self.cell(0, 6, "New York, New York 07008", ln=True, align='L')
self.ln(5) # Add a small space after the address
def create_invoice(business, index):
# Randomly decide if this invoice will have a large number of line items
if random.random() < 0.2: # 20% chance to have a large invoice
num_items = random.randint(25, 40) # Large invoice with 25-40 items
else:
num_items = random.randint(3, 10) # Standard invoice with 3-10 items
selected_items = random.sample(line_items * (num_items // len(line_items) + 1), k=num_items)
hourly_rates = [random.randint(50, 85) for _ in range(num_items)]
hours_worked = [round(random.uniform(0.25, 20), 2) for _ in range(num_items)]
# Calculate totals
subtotal = sum(rate * hours for rate, hours in zip(hourly_rates, hours_worked))
tax_rate = random.uniform(4, 10) / 100
tax = round(subtotal * tax_rate, 2)
total = round(subtotal + tax, 2)
# Ensure the amount paid is always partial (not the full amount)
amount_paid = random.choice([0, round(total / 4, 2), round(total / 2, 2)]) # Partial payments only
balance_due = total - amount_paid
# Create PDF
pdf = CustomInvoicePDF()
pdf.add_page()
# Invoice # stays at the top-right
pdf.set_font("Arial", style="B", size=14) # Bold and larger font size
pdf.set_xy(150, 10) # Set to the top-right corner
pdf.cell(50, 8, f"Invoice #: {random.randint(100000000, 999999999)}", ln=True, align='L')
# Move down 6 lines for the rest of the metadata
pdf.set_font("Arial", size=12)
pdf.set_y(10 + 36) # Start 6 lines below the top
pdf.set_x(150) # Reset X position for each line
pdf.cell(50, 6, f"Date: {datetime.now().strftime('%b %d, %Y')}", ln=True, align='L')
pdf.set_x(150)
pdf.cell(50, 6, "Payment Terms: 30 Days", ln=True, align='L')
pdf.set_x(150)
pdf.cell(50, 6, f"Due Date: {(datetime.now() + timedelta(days=30)).strftime('%b %d, %Y')}", ln=True, align='L')
# Balance Due: larger and bold
pdf.set_x(150) # Reset X position for Balance Due
pdf.set_font("Arial", style="B", size=12) # Bold and larger font size
pdf.cell(50, 8, f"Balance Due: ${balance_due:.2f}", ln=True, align='L')
pdf.ln(5) # Add small spacing after the metadata block
# Billing info with proper address formatting
pdf.set_font("Arial", size=12)
# Split the address into components based on commas
address_parts = business['address'].split(',') # Split by all commas
address_1 = address_parts[0].strip() # Street address
address_2 = ', '.join(part.strip() for part in address_parts[1:]) if len(address_parts) > 1 else "" # City, state, and zip
pdf.cell(100, 6, f"Bill To: {business['name']}", ln=True)
pdf.cell(100, 6, address_1, ln=True) # Street address
pdf.cell(100, 6, address_2, ln=True) # City, state, zip
pdf.cell(100, 6, f"Email: {business['email']}", ln=True)
pdf.ln(5) # Add a small gap after the Billing Info section
# Table header
pdf.set_font("Arial", style="B", size=10)
pdf.cell(115, 10, "Item", 1)
pdf.cell(20, 10, "Quantity", 1)
pdf.cell(20, 10, "Rate", 1)
pdf.cell(25, 10, "Amount", 1, ln=True)
# Table content with automatic page breaks
pdf.set_font("Arial", size=10)
for item, rate, hours in zip(selected_items, hourly_rates, hours_worked):
amount = rate * hours
# Add a page break if the current position is near the bottom
if pdf.get_y() > 260: # Roughly 260 units from the top for an 8.5x11 page
pdf.add_page()
# Re-add table header on new page
pdf.set_font("Arial", style="B", size=10)
pdf.cell(115, 10, "Item", 1)
pdf.cell(20, 10, "Quantity", 1)
pdf.cell(20, 10, "Rate", 1)
pdf.cell(25, 10, "Amount", 1, ln=True)
pdf.set_font("Arial", size=10) # Reset font for table content
pdf.cell(115, 6, item, 1)
pdf.cell(20, 6, f"{hours:.2f}", 1)
pdf.cell(20, 6, f"${rate:.2f}", 1)
pdf.cell(25, 6, f"${amount:.2f}", 1, ln=True)
# Totals without cell borders, aligned to the right near "Rate"
right_align_x = 100 # Adjust this value to align near "Rate"
pdf.set_x(right_align_x)
pdf.cell(50, 6, "Subtotal:", 0, align='R') # Title aligned to the right
pdf.cell(32, 6, f"${subtotal:.2f}", 0, ln=True, align='R') # Amount aligned to the right
pdf.set_x(right_align_x)
pdf.cell(50, 6, f"Tax ({tax_rate*100:.1f}%):", 0, align='R')
pdf.cell(32, 6, f"${tax:.2f}", 0, ln=True, align='R')
pdf.set_x(right_align_x)
pdf.cell(50, 6, "Total:", 0, align='R')
pdf.cell(32, 6, f"${total:.2f}", 0, ln=True, align='R')
pdf.set_x(right_align_x)
pdf.cell(50, 6, "Amount Paid:", 0, align='R')
pdf.cell(32, 6, f"${amount_paid:.2f}", 0, ln=True, align='R')
pdf.set_font("Arial", style="B", size=12) # Set font to bold
pdf.set_x(right_align_x)
pdf.cell(50, 6, "Balance Due:", 0, align='R')
pdf.cell(32, 6, f"${balance_due:.2f}", 0, ln=True, align='R')
# Notes
pdf.set_font("Arial", size=12)
pdf.ln(10)
pdf.cell(200, 10, "Notes:", ln=True)
pdf.multi_cell(0, 10, "Thanks for choosing the Py-Core.com Software Team.\nPayment is due within 30 days from date of invoice. Late payment is subject to fees of 5% per month.")
return pdf
# Output folder
output_dir = "invoices"
os.makedirs(output_dir, exist_ok=True)
# Generate invoices
for i, business in enumerate(random.choices(businesses, k=20), start=1):
pdf = create_invoice(business, i)
pdf.output(os.path.join(output_dir, f"invoice_{i:02d}.pdf"))
print("Invoices generated successfully in the 'invoices' folder.")

Thank you for reading this article. I hope you found it helpful and informative. If you have any questions, or if you would like to suggest new Python code examples or topics for future tutorials, please feel free to reach out. Your feedback and suggestions are always welcome!
Happy coding!
Py-Core.com Python Programming
You can also find this article at Medium.com