DEV Community

Cover image for How to generate dynamic invoices for SaaS applications in Python đź’¸
Ethan from reportgen.io
Ethan from reportgen.io

Posted on

How to generate dynamic invoices for SaaS applications in Python đź’¸

Invoice generation in SaaS applications doesn’t have to be tedious. With reportgen.io, you can automate the entire process, offering dynamic, scalable, and secure solutions. Whether you're working on a single invoice or handling bulk generation, reportgen.io’s modern API is built to make developers’ lives easier.

This guide walks you through the steps of integrating reportgen.io into your SaaS application for dynamic invoice generation, complete with advanced templating and efficient API calls.


Step 1: Define a Dynamic Template with Conditional Logic

Invoices are more than just a list of numbers; they must reflect accurate calculations, dynamic line items, and conditional elements like VAT. With reportgen.io, you can use templating engines such as EJS to create powerful, flexible templates.

Here’s an example template that:

  1. Iterates through line items dynamically.
  2. Calculates VAT conditionally based on the input percentage.
  3. Includes subtotals and totals.

EJS Template Example

<html>
  <body>
    <h1>Invoice for <%= CustomerName %></h1>
    <table border="1" style="border-collapse: collapse; width: 100%;">
      <thead>
        <tr>
          <th>Description</th>
          <th>Quantity</th>
          <th>Price</th>
          <th>Subtotal</th>
        </tr>
      </thead>
      <tbody>
        <% LineItems.forEach(item => { %>
          <tr>
            <td><%= item.description %></td>
            <td><%= item.quantity %></td>
            <td><%= item.price.toFixed(2) %></td>
            <td><%= (item.quantity * item.price).toFixed(2) %></td>
          </tr>
        <% }); %>
      </tbody>
    </table>
    <p>Subtotal: $<%= Subtotal.toFixed(2) %></p>
    <% if (VATPercentage > 0) { %>
      <p>VAT (<%= (VATPercentage * 100).toFixed(1) %>%): $<%= (Subtotal * VATPercentage).toFixed(2) %></p>
    <% } %>
    <p><strong>Total: $<%= Total.toFixed(2) %></strong></p>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

This template ensures flexibility and clarity, allowing you to dynamically populate all values, including VAT percentages and line items.


Step 2: Write Python Functions

To make the implementation reusable and scalable, we’ll create functions that:

  1. Dynamically calculate invoice data.
  2. Handle synchronous PDF generation for immediate needs.
  3. Use asynchronous generation for bulk operations.

Step 2.1: A Function to Generate Dynamic Data

This function calculates the subtotal, VAT, and total dynamically based on the provided line items and VAT percentage.

def generate_invoice_data(customer_name, line_items, vat_percentage):
    subtotal = sum(item['quantity'] * item['price'] for item in line_items)
    vat_amount = subtotal * vat_percentage
    total = subtotal + vat_amount
    return {
        "CustomerName": customer_name,
        "LineItems": line_items,
        "Subtotal": subtotal,
        "VATPercentage": vat_percentage,
        "Total": total
    }
Enter fullscreen mode Exit fullscreen mode

Step 2.2: A Function for Synchronous PDF Generation

Synchronous generation is best for smaller workloads or when you need the invoice immediately. This function sends the request and saves the PDF locally.

import requests

def generate_pdf_sync(api_key, template, data):
    url = "https://reportgen.io/api/v1/generate-pdf-sync"
    headers = {"X-API-Key": f"{api_key}", "Content-Type": "application/json"}
    payload = {"html_template": template, "data": data, "engine": "ejs"}

    response = requests.post(url, json=payload, headers=headers)
    if response.status_code == 200:
        with open("invoice_sync.pdf", "wb") as file:
            file.write(response.content)
        print("Invoice generated successfully as 'invoice_sync.pdf'.")
    else:
        print(f"Failed to generate PDF: {response.status_code} - {response.text}")
Enter fullscreen mode Exit fullscreen mode

Step 2.3: A Function for Asynchronous PDF Generation

Asynchronous generation is ideal for handling bulk operations or when the task isn’t time-sensitive. This function queues the PDF generation and returns a report_id for tracking.

def generate_pdf_async(api_key, template, data):
    url = "https://reportgen.io/api/v1/generate-pdf-async"
    headers = {"X-API-Key": f"{api_key}", "Content-Type": "application/json"}
    payload = {"html_template": template, "data": data, "engine": "ejs"}

    response = requests.post(url, json=payload, headers=headers)
    if response.status_code == 200:
        report_id = response.json().get("report_id")
        print(f"Invoice queued successfully. Report ID: {report_id}")
        return report_id
    else:
        print(f"Failed to queue PDF generation: {response.status_code} - {response.text}")
Enter fullscreen mode Exit fullscreen mode

Step 3: Usage

Example Input

Here’s an example of how to call the functions with dynamic data, but it doesn't include the functions. You can simply copy them over.

api_key = "YOUR_API_KEY"
template = """
<html>
  <body>
    <h1>Invoice for <%= CustomerName %></h1>
    <table border="1" style="border-collapse: collapse; width: 100%;">
      <thead>
        <tr>
          <th>Description</th>
          <th>Quantity</th>
          <th>Price</th>
          <th>Subtotal</th>
        </tr>
      </thead>
      <tbody>
        <% LineItems.forEach(item => { %>
          <tr>
            <td><%= item.description %></td>
            <td><%= item.quantity %></td>
            <td><%= item.price.toFixed(2) %></td>
            <td><%= (item.quantity * item.price).toFixed(2) %></td>
          </tr>
        <% }); %>
      </tbody>
    </table>
    <p>Subtotal: $<%= Subtotal.toFixed(2) %></p>
    <% if (VATPercentage > 0) { %>
      <p>VAT (<%= (VATPercentage * 100).toFixed(1) %>%): $<%= (Subtotal * VATPercentage).toFixed(2) %></p>
    <% } %>
    <p><strong>Total: $<%= Total.toFixed(2) %></strong></p>
  </body>
</html>
"""
line_items = [
    {"description": "DevOps monthly", "quantity": 1, "price": 5000.0},
    {"description": "SysAdmin hours", "quantity": 10, "price": 35.0},
]
vat_percentage = 0.22  # Dynamic VAT percentage
customer_name = "John Doe"

data = generate_invoice_data(customer_name, line_items, vat_percentage)

# Sync
generate_pdf_sync(api_key, template, data)

# Or async:
report_id = generate_pdf_async(api_key, template, data)
print(f"Track your report status using this ID: {report_id}")
Enter fullscreen mode Exit fullscreen mode

Why Choose reportgen.io?

  • Dynamic Powerhouse: Unlike competitors, reportgen.io empowers you to generate complex documents with dynamic data, conditional logic, and flexibility in templates.
  • Built for Scalability: Handle large volumes of PDF generation asynchronously without performance bottlenecks.
  • Developer-Centric: We prioritize ease of use, robust documentation, and advanced templating options like EJS and Handlebars.
  • Secure & Modern: With Cloudflare-backed security and data privacy, reportgen.io ensures your sensitive data is always protected.
  • Competitive Edge: reportgen.io offers simplicity without compromising on advanced features and flexibility, giving you the best of both worlds.

Next Steps

  • Implement integrations with services like Novu.co for automated invoice emailing.
  • Attach payment links using Stripe or PayPal APIs for seamless transactions.
  • Enhance workflows by incorporating invoice tracking and CRM updates with HubSpot or Salesforce.

Start leveraging reportgen.io to elevate your SaaS application's automation game today! 🚀


đź’» Source code can be found on GitHub

👉 Ready to make your PDFs not suck? Try it free at reportgen.io
You get 150 reports for free each month, no credit card required.

👥 Have a better way to write this code? Drop it in the comments below. I always love a good code review!

Top comments (0)